superqode 0.1.5__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 (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,781 @@
1
+ """
2
+ QR Web Dashboard - Interactive local viewer for Quality Reports.
3
+
4
+ Provides a web-based interface for viewing QR findings with:
5
+ - Severity filtering
6
+ - Interactive findings table
7
+ - Verified fixes visualization
8
+ - Trend charts from historical QRs
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import http.server
14
+ import json
15
+ import socketserver
16
+ import threading
17
+ import webbrowser
18
+ from datetime import datetime
19
+ from pathlib import Path
20
+ from typing import Any, Dict, List, Optional
21
+
22
+
23
+ # HTML template with dark theme matching SuperQode TUI
24
+ HTML_TEMPLATE = """<!DOCTYPE html>
25
+ <html lang="en">
26
+ <head>
27
+ <meta charset="UTF-8">
28
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
29
+ <title>SuperQode QR Dashboard</title>
30
+ <style>
31
+ :root {
32
+ --bg-primary: #0a0a0a;
33
+ --bg-secondary: #1a1a1a;
34
+ --bg-tertiary: #2a2a2a;
35
+ --text-primary: #e4e4e7;
36
+ --text-secondary: #a1a1aa;
37
+ --text-muted: #71717a;
38
+ --purple: #a855f7;
39
+ --pink: #ec4899;
40
+ --orange: #f97316;
41
+ --cyan: #06b6d4;
42
+ --green: #22c55e;
43
+ --yellow: #fbbf24;
44
+ --red: #ef4444;
45
+ --blue: #3b82f6;
46
+ }
47
+
48
+ * {
49
+ margin: 0;
50
+ padding: 0;
51
+ box-sizing: border-box;
52
+ }
53
+
54
+ body {
55
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
56
+ background: var(--bg-primary);
57
+ color: var(--text-primary);
58
+ line-height: 1.6;
59
+ min-height: 100vh;
60
+ }
61
+
62
+ .container {
63
+ max-width: 1400px;
64
+ margin: 0 auto;
65
+ padding: 2rem;
66
+ }
67
+
68
+ /* Header */
69
+ .header {
70
+ display: flex;
71
+ justify-content: space-between;
72
+ align-items: center;
73
+ margin-bottom: 2rem;
74
+ padding-bottom: 1rem;
75
+ border-bottom: 1px solid var(--bg-tertiary);
76
+ }
77
+
78
+ .logo {
79
+ font-size: 1.5rem;
80
+ font-weight: bold;
81
+ background: linear-gradient(90deg, var(--purple), var(--pink), var(--orange));
82
+ -webkit-background-clip: text;
83
+ -webkit-text-fill-color: transparent;
84
+ background-clip: text;
85
+ }
86
+
87
+ .meta {
88
+ text-align: right;
89
+ color: var(--text-secondary);
90
+ font-size: 0.875rem;
91
+ }
92
+
93
+ /* Verdict Banner */
94
+ .verdict {
95
+ padding: 1.5rem;
96
+ border-radius: 0.5rem;
97
+ margin-bottom: 2rem;
98
+ text-align: center;
99
+ }
100
+
101
+ .verdict.pass {
102
+ background: linear-gradient(135deg, rgba(34, 197, 94, 0.2), rgba(34, 197, 94, 0.05));
103
+ border: 1px solid var(--green);
104
+ }
105
+
106
+ .verdict.conditional {
107
+ background: linear-gradient(135deg, rgba(251, 191, 36, 0.2), rgba(251, 191, 36, 0.05));
108
+ border: 1px solid var(--yellow);
109
+ }
110
+
111
+ .verdict.fail {
112
+ background: linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(239, 68, 68, 0.05));
113
+ border: 1px solid var(--red);
114
+ }
115
+
116
+ .verdict.blocked {
117
+ background: linear-gradient(135deg, rgba(113, 113, 122, 0.2), rgba(113, 113, 122, 0.05));
118
+ border: 1px solid var(--text-muted);
119
+ }
120
+
121
+ .verdict h2 {
122
+ font-size: 1.5rem;
123
+ margin-bottom: 0.5rem;
124
+ }
125
+
126
+ .verdict p {
127
+ color: var(--text-secondary);
128
+ }
129
+
130
+ /* Stats Grid */
131
+ .stats-grid {
132
+ display: grid;
133
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
134
+ gap: 1rem;
135
+ margin-bottom: 2rem;
136
+ }
137
+
138
+ .stat-card {
139
+ background: var(--bg-secondary);
140
+ padding: 1rem;
141
+ border-radius: 0.5rem;
142
+ text-align: center;
143
+ }
144
+
145
+ .stat-card .value {
146
+ font-size: 2rem;
147
+ font-weight: bold;
148
+ }
149
+
150
+ .stat-card .label {
151
+ color: var(--text-secondary);
152
+ font-size: 0.75rem;
153
+ text-transform: uppercase;
154
+ }
155
+
156
+ .stat-card.critical .value { color: var(--red); }
157
+ .stat-card.high .value { color: var(--orange); }
158
+ .stat-card.medium .value { color: var(--yellow); }
159
+ .stat-card.low .value { color: var(--blue); }
160
+ .stat-card.info .value { color: var(--text-muted); }
161
+
162
+ /* Filters */
163
+ .filters {
164
+ display: flex;
165
+ gap: 0.5rem;
166
+ margin-bottom: 1.5rem;
167
+ flex-wrap: wrap;
168
+ }
169
+
170
+ .filter-btn {
171
+ padding: 0.5rem 1rem;
172
+ border: 1px solid var(--bg-tertiary);
173
+ background: var(--bg-secondary);
174
+ color: var(--text-primary);
175
+ border-radius: 0.25rem;
176
+ cursor: pointer;
177
+ font-family: inherit;
178
+ font-size: 0.875rem;
179
+ transition: all 0.2s;
180
+ }
181
+
182
+ .filter-btn:hover {
183
+ background: var(--bg-tertiary);
184
+ }
185
+
186
+ .filter-btn.active {
187
+ background: var(--purple);
188
+ border-color: var(--purple);
189
+ }
190
+
191
+ /* Findings Table */
192
+ .section {
193
+ margin-bottom: 2rem;
194
+ }
195
+
196
+ .section h3 {
197
+ font-size: 1.125rem;
198
+ margin-bottom: 1rem;
199
+ color: var(--purple);
200
+ }
201
+
202
+ .findings-table {
203
+ width: 100%;
204
+ border-collapse: collapse;
205
+ background: var(--bg-secondary);
206
+ border-radius: 0.5rem;
207
+ overflow: hidden;
208
+ }
209
+
210
+ .findings-table th,
211
+ .findings-table td {
212
+ padding: 0.75rem 1rem;
213
+ text-align: left;
214
+ border-bottom: 1px solid var(--bg-tertiary);
215
+ }
216
+
217
+ .findings-table th {
218
+ background: var(--bg-tertiary);
219
+ color: var(--text-secondary);
220
+ font-weight: 500;
221
+ font-size: 0.75rem;
222
+ text-transform: uppercase;
223
+ }
224
+
225
+ .findings-table tr:last-child td {
226
+ border-bottom: none;
227
+ }
228
+
229
+ .findings-table tr:hover {
230
+ background: var(--bg-tertiary);
231
+ }
232
+
233
+ .severity-badge {
234
+ display: inline-block;
235
+ padding: 0.25rem 0.5rem;
236
+ border-radius: 0.25rem;
237
+ font-size: 0.75rem;
238
+ font-weight: 500;
239
+ text-transform: uppercase;
240
+ }
241
+
242
+ .severity-badge.critical { background: rgba(239, 68, 68, 0.2); color: var(--red); }
243
+ .severity-badge.high { background: rgba(249, 115, 22, 0.2); color: var(--orange); }
244
+ .severity-badge.medium { background: rgba(251, 191, 36, 0.2); color: var(--yellow); }
245
+ .severity-badge.low { background: rgba(59, 130, 246, 0.2); color: var(--blue); }
246
+ .severity-badge.info { background: rgba(113, 113, 122, 0.2); color: var(--text-muted); }
247
+
248
+ .priority-badge {
249
+ display: inline-block;
250
+ padding: 0.125rem 0.375rem;
251
+ background: var(--bg-tertiary);
252
+ border-radius: 0.25rem;
253
+ font-size: 0.625rem;
254
+ color: var(--text-secondary);
255
+ }
256
+
257
+ .location {
258
+ font-size: 0.875rem;
259
+ color: var(--cyan);
260
+ }
261
+
262
+ .confidence {
263
+ font-size: 0.75rem;
264
+ color: var(--text-muted);
265
+ }
266
+
267
+ /* Finding Details Modal */
268
+ .modal-overlay {
269
+ display: none;
270
+ position: fixed;
271
+ top: 0;
272
+ left: 0;
273
+ right: 0;
274
+ bottom: 0;
275
+ background: rgba(0, 0, 0, 0.8);
276
+ z-index: 1000;
277
+ justify-content: center;
278
+ align-items: center;
279
+ }
280
+
281
+ .modal-overlay.active {
282
+ display: flex;
283
+ }
284
+
285
+ .modal {
286
+ background: var(--bg-secondary);
287
+ border-radius: 0.5rem;
288
+ max-width: 800px;
289
+ max-height: 80vh;
290
+ overflow: auto;
291
+ padding: 1.5rem;
292
+ margin: 1rem;
293
+ }
294
+
295
+ .modal h4 {
296
+ margin-bottom: 1rem;
297
+ color: var(--purple);
298
+ }
299
+
300
+ .modal-close {
301
+ float: right;
302
+ background: none;
303
+ border: none;
304
+ color: var(--text-secondary);
305
+ cursor: pointer;
306
+ font-size: 1.5rem;
307
+ }
308
+
309
+ .modal pre {
310
+ background: var(--bg-primary);
311
+ padding: 1rem;
312
+ border-radius: 0.25rem;
313
+ overflow-x: auto;
314
+ font-size: 0.875rem;
315
+ }
316
+
317
+ /* Verified Fixes Section */
318
+ .fix-card {
319
+ background: var(--bg-secondary);
320
+ border-radius: 0.5rem;
321
+ padding: 1rem;
322
+ margin-bottom: 1rem;
323
+ }
324
+
325
+ .fix-card.improvement {
326
+ border-left: 3px solid var(--green);
327
+ }
328
+
329
+ .fix-card.failed {
330
+ border-left: 3px solid var(--red);
331
+ }
332
+
333
+ .fix-header {
334
+ display: flex;
335
+ justify-content: space-between;
336
+ align-items: center;
337
+ margin-bottom: 0.5rem;
338
+ }
339
+
340
+ .fix-metrics {
341
+ display: flex;
342
+ gap: 1rem;
343
+ color: var(--text-secondary);
344
+ font-size: 0.875rem;
345
+ }
346
+
347
+ .fix-metrics .positive { color: var(--green); }
348
+ .fix-metrics .negative { color: var(--red); }
349
+
350
+ /* Empty State */
351
+ .empty-state {
352
+ text-align: center;
353
+ padding: 3rem;
354
+ color: var(--text-muted);
355
+ }
356
+
357
+ .empty-state .icon {
358
+ font-size: 3rem;
359
+ margin-bottom: 1rem;
360
+ }
361
+
362
+ /* Footer */
363
+ .footer {
364
+ text-align: center;
365
+ padding: 2rem;
366
+ color: var(--text-muted);
367
+ font-size: 0.75rem;
368
+ border-top: 1px solid var(--bg-tertiary);
369
+ margin-top: 2rem;
370
+ }
371
+
372
+ .footer a {
373
+ color: var(--purple);
374
+ text-decoration: none;
375
+ }
376
+ </style>
377
+ </head>
378
+ <body>
379
+ <div class="container">
380
+ <header class="header">
381
+ <div class="logo">SuperQode QR Dashboard</div>
382
+ <div class="meta">
383
+ <div>Session: <code>{session_id}</code></div>
384
+ <div>Date: {date}</div>
385
+ <div>Duration: {duration}s</div>
386
+ </div>
387
+ </header>
388
+
389
+ <div class="verdict {verdict_class}">
390
+ <h2>{verdict_icon} {verdict_text}</h2>
391
+ <p>{verdict_explanation}</p>
392
+ </div>
393
+
394
+ <div class="stats-grid">
395
+ <div class="stat-card critical">
396
+ <div class="value">{critical_count}</div>
397
+ <div class="label">Critical</div>
398
+ </div>
399
+ <div class="stat-card high">
400
+ <div class="value">{high_count}</div>
401
+ <div class="label">High</div>
402
+ </div>
403
+ <div class="stat-card medium">
404
+ <div class="value">{medium_count}</div>
405
+ <div class="label">Medium</div>
406
+ </div>
407
+ <div class="stat-card low">
408
+ <div class="value">{low_count}</div>
409
+ <div class="label">Low</div>
410
+ </div>
411
+ <div class="stat-card info">
412
+ <div class="value">{info_count}</div>
413
+ <div class="label">Info</div>
414
+ </div>
415
+ </div>
416
+
417
+ <div class="section">
418
+ <h3>Findings ({total_findings})</h3>
419
+
420
+ <div class="filters">
421
+ <button class="filter-btn active" onclick="filterFindings('all')">All</button>
422
+ <button class="filter-btn" onclick="filterFindings('critical')">Critical</button>
423
+ <button class="filter-btn" onclick="filterFindings('high')">High</button>
424
+ <button class="filter-btn" onclick="filterFindings('medium')">Medium</button>
425
+ <button class="filter-btn" onclick="filterFindings('low')">Low</button>
426
+ <button class="filter-btn" onclick="filterFindings('info')">Info</button>
427
+ </div>
428
+
429
+ {findings_table}
430
+ </div>
431
+
432
+ {verified_fixes_section}
433
+
434
+ <footer class="footer">
435
+ Generated by <a href="https://github.com/superqode/superqode">SuperQode</a> - Agentic Quality Engineering
436
+ </footer>
437
+ </div>
438
+
439
+ <div class="modal-overlay" id="modalOverlay" onclick="closeModal(event)">
440
+ <div class="modal" onclick="event.stopPropagation()">
441
+ <button class="modal-close" onclick="closeModal()">&times;</button>
442
+ <div id="modalContent"></div>
443
+ </div>
444
+ </div>
445
+
446
+ <script>
447
+ const findings = {findings_json};
448
+
449
+ function filterFindings(severity) {
450
+ document.querySelectorAll('.filter-btn').forEach(btn => btn.classList.remove('active'));
451
+ event.target.classList.add('active');
452
+
453
+ document.querySelectorAll('.finding-row').forEach(row => {
454
+ if (severity === 'all' || row.dataset.severity === severity) {
455
+ row.style.display = '';
456
+ } else {
457
+ row.style.display = 'none';
458
+ }
459
+ });
460
+ }
461
+
462
+ function showFindingDetails(id) {
463
+ const finding = findings.find(f => f.id === id);
464
+ if (!finding) return;
465
+
466
+ let html = `
467
+ <h4>${finding.title}</h4>
468
+ <p><span class="severity-badge ${finding.severity}">${finding.severity}</span>
469
+ <span class="priority-badge">P${finding.priority}</span></p>
470
+ <p style="margin: 1rem 0;">${finding.description}</p>
471
+ `;
472
+
473
+ if (finding.location) {
474
+ html += `<p><strong>Location:</strong> <code class="location">${finding.location}</code></p>`;
475
+ }
476
+
477
+ if (finding.evidence) {
478
+ html += `<p style="margin-top: 1rem;"><strong>Evidence:</strong></p>
479
+ <pre>${escapeHtml(finding.evidence)}</pre>`;
480
+ }
481
+
482
+ if (finding.suggested_fix) {
483
+ html += `<p style="margin-top: 1rem;"><strong>Suggested Fix:</strong></p>
484
+ <pre>${escapeHtml(finding.suggested_fix)}</pre>`;
485
+ }
486
+
487
+ if (finding.tags && finding.tags.length > 0) {
488
+ html += `<p style="margin-top: 1rem;"><strong>Tags:</strong> ${finding.tags.join(', ')}</p>`;
489
+ }
490
+
491
+ document.getElementById('modalContent').innerHTML = html;
492
+ document.getElementById('modalOverlay').classList.add('active');
493
+ }
494
+
495
+ function closeModal(event) {
496
+ if (!event || event.target.id === 'modalOverlay') {
497
+ document.getElementById('modalOverlay').classList.remove('active');
498
+ }
499
+ }
500
+
501
+ function escapeHtml(text) {
502
+ const div = document.createElement('div');
503
+ div.textContent = text;
504
+ return div.innerHTML;
505
+ }
506
+
507
+ // Close modal on Escape key
508
+ document.addEventListener('keydown', (e) => {
509
+ if (e.key === 'Escape') closeModal();
510
+ });
511
+ </script>
512
+ </body>
513
+ </html>
514
+ """
515
+
516
+
517
+ def _render_findings_table(findings: List[Dict[str, Any]]) -> str:
518
+ """Render the findings table HTML."""
519
+ if not findings:
520
+ return """
521
+ <div class="empty-state">
522
+ <div class="icon">✅</div>
523
+ <p>No issues found during this investigation.</p>
524
+ </div>
525
+ """
526
+
527
+ rows = []
528
+ for f in findings:
529
+ severity = f.get("severity", "info")
530
+ priority = f.get("priority", 2)
531
+ confidence = f.get("confidence_score", 0.8)
532
+ location = f.get("location", "-")
533
+
534
+ rows.append(f"""
535
+ <tr class="finding-row" data-severity="{severity}" onclick="showFindingDetails('{f["id"]}')">
536
+ <td><span class="severity-badge {severity}">{severity}</span></td>
537
+ <td><span class="priority-badge">P{priority}</span></td>
538
+ <td>{f.get("title", "Untitled")}</td>
539
+ <td><span class="location">{location}</span></td>
540
+ <td><span class="confidence">{confidence:.0%}</span></td>
541
+ <td>{f.get("found_by", "-")}</td>
542
+ </tr>
543
+ """)
544
+
545
+ return f"""
546
+ <table class="findings-table">
547
+ <thead>
548
+ <tr>
549
+ <th>Severity</th>
550
+ <th>Priority</th>
551
+ <th>Title</th>
552
+ <th>Location</th>
553
+ <th>Confidence</th>
554
+ <th>Found By</th>
555
+ </tr>
556
+ </thead>
557
+ <tbody>
558
+ {"".join(rows)}
559
+ </tbody>
560
+ </table>
561
+ """
562
+
563
+
564
+ def _render_verified_fixes(verified_fixes: Optional[Dict[str, Any]]) -> str:
565
+ """Render the verified fixes section."""
566
+ if not verified_fixes or not verified_fixes.get("fixes"):
567
+ return ""
568
+
569
+ fixes = verified_fixes.get("fixes", [])
570
+ total = verified_fixes.get("total", 0)
571
+ verified = verified_fixes.get("verified", 0)
572
+ improvements = verified_fixes.get("improvements", 0)
573
+
574
+ cards = []
575
+ for fix in fixes:
576
+ is_improvement = fix.get("is_improvement", False)
577
+ card_class = "improvement" if is_improvement else "failed"
578
+ status = "✅ Verified" if fix.get("fix_verified") else "❌ Failed"
579
+
580
+ metrics = fix.get("metrics", {})
581
+ before = metrics.get("before", {})
582
+ after = metrics.get("after", {})
583
+
584
+ tests_before = f"{before.get('tests_passed', 0)}/{before.get('tests_total', 0)}"
585
+ tests_after = f"{after.get('tests_passed', 0)}/{after.get('tests_total', 0)}"
586
+
587
+ cards.append(f"""
588
+ <div class="fix-card {card_class}">
589
+ <div class="fix-header">
590
+ <strong>{fix.get("finding_title", "Unknown")}</strong>
591
+ <span>{status}</span>
592
+ </div>
593
+ <div class="fix-metrics">
594
+ <span>Tests: {tests_before} → <span class="{"positive" if is_improvement else ""}">{tests_after}</span></span>
595
+ <span>Patch: <code>{fix.get("patch_file", "-")}</code></span>
596
+ <span>Confidence: {fix.get("fix_confidence", 0):.0%}</span>
597
+ </div>
598
+ </div>
599
+ """)
600
+
601
+ return f"""
602
+ <div class="section">
603
+ <h3>Verified Fixes ({verified}/{total} verified, {improvements} improvements)</h3>
604
+ {"".join(cards)}
605
+ </div>
606
+ """
607
+
608
+
609
+ def generate_dashboard_html(qir_json: Dict[str, Any]) -> str:
610
+ """Generate HTML dashboard from QIR JSON data."""
611
+ # Extract data
612
+ session_id = qir_json.get("session_id", "unknown")[:12]
613
+ mode = qir_json.get("mode", "unknown")
614
+ duration = qir_json.get("duration_seconds", 0)
615
+
616
+ # Parse date
617
+ started_at = qir_json.get("started_at", "")
618
+ try:
619
+ date = datetime.fromisoformat(started_at.replace("Z", "+00:00")).strftime("%Y-%m-%d %H:%M")
620
+ except (ValueError, AttributeError):
621
+ date = started_at[:16] if started_at else "Unknown"
622
+
623
+ # Verdict
624
+ verdict = qir_json.get("verdict", "blocked")
625
+ verdict_map = {
626
+ "pass": ("✅", "PASS", "pass", "No significant issues found"),
627
+ "conditional": ("⚠️", "CONDITIONAL PASS", "conditional", "Issues found, review recommended"),
628
+ "fail": ("❌", "FAIL", "fail", "Critical issues require attention"),
629
+ "blocked": ("⛔", "BLOCKED", "blocked", "Analysis could not complete"),
630
+ }
631
+ verdict_icon, verdict_text, verdict_class, verdict_explanation = verdict_map.get(
632
+ verdict, verdict_map["blocked"]
633
+ )
634
+
635
+ # Override explanation if provided
636
+ if qir_json.get("overall_explanation"):
637
+ verdict_explanation = qir_json["overall_explanation"]
638
+
639
+ # Summary counts
640
+ summary = qir_json.get("summary", {})
641
+ by_severity = summary.get("by_severity", {})
642
+
643
+ critical_count = by_severity.get("critical", 0)
644
+ high_count = by_severity.get("high", 0)
645
+ medium_count = by_severity.get("medium", 0)
646
+ low_count = by_severity.get("low", 0)
647
+ info_count = by_severity.get("info", 0)
648
+ total_findings = summary.get("total_findings", 0)
649
+
650
+ # Findings
651
+ findings = qir_json.get("findings", [])
652
+ findings_table = _render_findings_table(findings)
653
+ findings_json = json.dumps(findings)
654
+
655
+ # Verified fixes
656
+ verified_fixes = qir_json.get("verified_fixes")
657
+ verified_fixes_section = _render_verified_fixes(verified_fixes)
658
+
659
+ # Render template
660
+ return HTML_TEMPLATE.format(
661
+ session_id=session_id,
662
+ date=date,
663
+ duration=f"{duration:.1f}",
664
+ verdict_icon=verdict_icon,
665
+ verdict_text=verdict_text,
666
+ verdict_class=verdict_class,
667
+ verdict_explanation=verdict_explanation,
668
+ critical_count=critical_count,
669
+ high_count=high_count,
670
+ medium_count=medium_count,
671
+ low_count=low_count,
672
+ info_count=info_count,
673
+ total_findings=total_findings,
674
+ findings_table=findings_table,
675
+ findings_json=findings_json,
676
+ verified_fixes_section=verified_fixes_section,
677
+ )
678
+
679
+
680
+ class QIRDashboardHandler(http.server.SimpleHTTPRequestHandler):
681
+ """HTTP handler for serving QIR dashboard."""
682
+
683
+ html_content: str = ""
684
+
685
+ def do_GET(self):
686
+ """Handle GET requests."""
687
+ self.send_response(200)
688
+ self.send_header("Content-type", "text/html; charset=utf-8")
689
+ self.send_header("Cache-Control", "no-cache")
690
+ self.end_headers()
691
+ self.wfile.write(self.html_content.encode("utf-8"))
692
+
693
+ def log_message(self, format: str, *args):
694
+ """Suppress default logging."""
695
+ pass
696
+
697
+
698
+ def find_latest_qr(project_root: Path) -> Optional[Path]:
699
+ """Find the most recent QR JSON file."""
700
+ qr_dir = project_root / ".superqode" / "qe-artifacts" / "qr"
701
+
702
+ if not qr_dir.exists():
703
+ return None
704
+
705
+ json_files = list(qr_dir.glob("*.json"))
706
+ if not json_files:
707
+ return None
708
+
709
+ return max(json_files, key=lambda f: f.stat().st_mtime)
710
+
711
+
712
+ def start_dashboard(
713
+ qr_path: Optional[Path] = None,
714
+ project_root: Optional[Path] = None,
715
+ port: int = 8765,
716
+ open_browser: bool = True,
717
+ ) -> None:
718
+ """
719
+ Start the QR web dashboard.
720
+
721
+ Args:
722
+ qr_path: Path to specific QR JSON file. If None, uses latest.
723
+ project_root: Project root to search for QRs. Defaults to cwd.
724
+ port: Port to serve on (default: 8765)
725
+ open_browser: Whether to open browser automatically
726
+ """
727
+ project_root = project_root or Path.cwd()
728
+
729
+ # Find QR file
730
+ if qr_path is None:
731
+ qr_path = find_latest_qr(project_root)
732
+ if qr_path is None:
733
+ raise FileNotFoundError("No QR files found. Run 'superqe run .' first.")
734
+
735
+ # Load QR JSON
736
+ qr_json = json.loads(qr_path.read_text())
737
+
738
+ # Generate HTML
739
+ html_content = generate_dashboard_html(qir_json)
740
+
741
+ # Configure handler
742
+ QIRDashboardHandler.html_content = html_content
743
+
744
+ # Start server
745
+ with socketserver.TCPServer(("", port), QIRDashboardHandler) as httpd:
746
+ url = f"http://localhost:{port}"
747
+ print(f"QR Dashboard running at {url}")
748
+ print("Press Ctrl+C to stop")
749
+
750
+ if open_browser:
751
+ # Open browser in background thread to not block
752
+ threading.Timer(0.5, lambda: webbrowser.open(url)).start()
753
+
754
+ try:
755
+ httpd.serve_forever()
756
+ except KeyboardInterrupt:
757
+ print("\nDashboard stopped")
758
+
759
+
760
+ def export_html(
761
+ qr_path: Path,
762
+ output_path: Optional[Path] = None,
763
+ ) -> Path:
764
+ """
765
+ Export QR as standalone HTML file.
766
+
767
+ Args:
768
+ qr_path: Path to QR JSON file
769
+ output_path: Output path for HTML. Defaults to same name with .html extension.
770
+
771
+ Returns:
772
+ Path to generated HTML file
773
+ """
774
+ qr_json = json.loads(qr_path.read_text())
775
+ html_content = generate_dashboard_html(qir_json)
776
+
777
+ if output_path is None:
778
+ output_path = qr_path.with_suffix(".html")
779
+
780
+ output_path.write_text(html_content, encoding="utf-8")
781
+ return output_path