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,356 @@
1
+ """
2
+ Code Context Viewer Widget.
3
+
4
+ A SuperQode-original widget for displaying code with contextual
5
+ highlighting, inline diffs, and related issue information.
6
+
7
+ Design: Smart code display that emphasizes the specific lines
8
+ where issues were found, with collapsible related findings.
9
+ """
10
+
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+ from typing import Dict, List, Optional, Tuple
14
+
15
+ from rich.console import RenderableType
16
+ from rich.panel import Panel
17
+ from rich.syntax import Syntax
18
+ from rich.text import Text
19
+ from textual.reactive import reactive
20
+ from textual.widgets import Static
21
+
22
+
23
+ class LineType(Enum):
24
+ """Type of line in the context view."""
25
+
26
+ CONTEXT = "context" # Normal context line
27
+ ADDED = "added" # Added line (green)
28
+ REMOVED = "removed" # Removed line (red)
29
+ ISSUE = "issue" # Line with issue (highlighted)
30
+ FOCUS = "focus" # The main focus line
31
+
32
+
33
+ @dataclass
34
+ class CodeLine:
35
+ """A single line of code with metadata."""
36
+
37
+ number: int
38
+ content: str
39
+ line_type: LineType = LineType.CONTEXT
40
+ annotation: str = "" # Inline annotation (e.g., "← HERE")
41
+
42
+ @property
43
+ def prefix(self) -> str:
44
+ """Get the line prefix based on type."""
45
+ if self.line_type == LineType.ADDED:
46
+ return "+"
47
+ elif self.line_type == LineType.REMOVED:
48
+ return "-"
49
+ else:
50
+ return " "
51
+
52
+
53
+ @dataclass
54
+ class RelatedFinding:
55
+ """A related finding in other parts of the codebase."""
56
+
57
+ file_path: str
58
+ line_number: int
59
+ description: str
60
+ similarity: float = 0.0 # 0.0 to 1.0
61
+
62
+
63
+ @dataclass
64
+ class CodeContext:
65
+ """Context for displaying code with an issue."""
66
+
67
+ file_path: str
68
+ language: str
69
+ issue_title: str
70
+ issue_description: str = ""
71
+ lines: List[CodeLine] = field(default_factory=list)
72
+ focus_line: Optional[int] = None
73
+ related_findings: List[RelatedFinding] = field(default_factory=list)
74
+
75
+
76
+ # Language detection from file extension
77
+ LANGUAGE_MAP = {
78
+ ".py": "python",
79
+ ".js": "javascript",
80
+ ".ts": "typescript",
81
+ ".tsx": "typescript",
82
+ ".jsx": "javascript",
83
+ ".java": "java",
84
+ ".go": "go",
85
+ ".rs": "rust",
86
+ ".rb": "ruby",
87
+ ".php": "php",
88
+ ".c": "c",
89
+ ".cpp": "cpp",
90
+ ".h": "c",
91
+ ".hpp": "cpp",
92
+ ".cs": "csharp",
93
+ ".swift": "swift",
94
+ ".kt": "kotlin",
95
+ ".scala": "scala",
96
+ ".sh": "bash",
97
+ ".bash": "bash",
98
+ ".zsh": "zsh",
99
+ ".yaml": "yaml",
100
+ ".yml": "yaml",
101
+ ".json": "json",
102
+ ".xml": "xml",
103
+ ".html": "html",
104
+ ".css": "css",
105
+ ".scss": "scss",
106
+ ".sql": "sql",
107
+ ".md": "markdown",
108
+ ".toml": "toml",
109
+ }
110
+
111
+
112
+ def detect_language(file_path: str) -> str:
113
+ """Detect language from file path."""
114
+ for ext, lang in LANGUAGE_MAP.items():
115
+ if file_path.endswith(ext):
116
+ return lang
117
+ return "text"
118
+
119
+
120
+ class CodeContextViewer(Static):
121
+ """Code Context Viewer Widget.
122
+
123
+ Displays code with contextual highlighting around issues,
124
+ showing before/after diffs and related findings.
125
+
126
+ Usage:
127
+ viewer = CodeContextViewer()
128
+ viewer.set_context(CodeContext(
129
+ file_path="src/api/user.py",
130
+ language="python",
131
+ issue_title="Unhandled null reference",
132
+ lines=[
133
+ CodeLine(23, "def get_user(user_id: str):"),
134
+ CodeLine(24, " user = db.find(user_id)"),
135
+ CodeLine(25, " return user.name", LineType.ISSUE, "← HERE"),
136
+ ],
137
+ focus_line=25,
138
+ ))
139
+ """
140
+
141
+ DEFAULT_CSS = """
142
+ CodeContextViewer {
143
+ height: auto;
144
+ border: solid #3f3f46;
145
+ padding: 0 1;
146
+ margin: 0 0 1 0;
147
+ }
148
+ """
149
+
150
+ # Reactive state
151
+ show_related: reactive[bool] = reactive(False)
152
+
153
+ def __init__(
154
+ self,
155
+ title: str = "Code Context",
156
+ show_line_numbers: bool = True,
157
+ context_lines: int = 3,
158
+ **kwargs,
159
+ ):
160
+ super().__init__(**kwargs)
161
+ self.title = title
162
+ self.show_line_numbers = show_line_numbers
163
+ self.context_lines = context_lines
164
+ self._context: Optional[CodeContext] = None
165
+
166
+ def set_context(self, context: CodeContext) -> None:
167
+ """Set the code context to display."""
168
+ self._context = context
169
+ self.refresh()
170
+
171
+ def set_from_diff(
172
+ self,
173
+ file_path: str,
174
+ issue_title: str,
175
+ before: str,
176
+ after: str,
177
+ focus_line: Optional[int] = None,
178
+ ) -> None:
179
+ """Set context from a before/after diff."""
180
+ # Simple diff - just show the change
181
+ before_lines = before.splitlines() if before else []
182
+ after_lines = after.splitlines() if after else []
183
+
184
+ lines = []
185
+ language = detect_language(file_path)
186
+
187
+ # Build a simple unified view
188
+ max_lines = max(len(before_lines), len(after_lines))
189
+
190
+ for i in range(max_lines):
191
+ line_num = i + 1
192
+ before_line = before_lines[i] if i < len(before_lines) else None
193
+ after_line = after_lines[i] if i < len(after_lines) else None
194
+
195
+ if before_line == after_line:
196
+ # Unchanged
197
+ lines.append(CodeLine(line_num, after_line or "", LineType.CONTEXT))
198
+ elif before_line is None:
199
+ # Added
200
+ lines.append(CodeLine(line_num, after_line, LineType.ADDED))
201
+ elif after_line is None:
202
+ # Removed
203
+ lines.append(CodeLine(line_num, before_line, LineType.REMOVED))
204
+ else:
205
+ # Changed - show both
206
+ lines.append(CodeLine(line_num, before_line, LineType.REMOVED))
207
+ lines.append(CodeLine(line_num, after_line, LineType.ADDED))
208
+
209
+ self._context = CodeContext(
210
+ file_path=file_path,
211
+ language=language,
212
+ issue_title=issue_title,
213
+ lines=lines,
214
+ focus_line=focus_line,
215
+ )
216
+ self.refresh()
217
+
218
+ def add_related_finding(self, finding: RelatedFinding) -> None:
219
+ """Add a related finding."""
220
+ if self._context:
221
+ self._context.related_findings.append(finding)
222
+ self.refresh()
223
+
224
+ def toggle_related(self) -> None:
225
+ """Toggle visibility of related findings."""
226
+ self.show_related = not self.show_related
227
+
228
+ def clear(self) -> None:
229
+ """Clear the context."""
230
+ self._context = None
231
+ self.refresh()
232
+
233
+ def _get_line_style(self, line: CodeLine) -> Tuple[str, str]:
234
+ """Get style and prefix for a line type."""
235
+ if line.line_type == LineType.ADDED:
236
+ return "#22c55e", "+"
237
+ elif line.line_type == LineType.REMOVED:
238
+ return "#ef4444", "-"
239
+ elif line.line_type == LineType.ISSUE:
240
+ return "#eab308", "!"
241
+ elif line.line_type == LineType.FOCUS:
242
+ return "#3b82f6", "▶"
243
+ else:
244
+ return "#a1a1aa", " "
245
+
246
+ def _render_line(self, line: CodeLine, max_num_width: int) -> Text:
247
+ """Render a single code line."""
248
+ color, prefix = self._get_line_style(line)
249
+
250
+ result = Text()
251
+
252
+ # Line number (right-aligned)
253
+ if self.show_line_numbers:
254
+ num_str = str(line.number).rjust(max_num_width)
255
+ result.append(f"{num_str} │", style="#6b7280")
256
+
257
+ # Prefix (+/-/!)
258
+ result.append(prefix, style=f"bold {color}")
259
+
260
+ # Content with background for changes
261
+ if line.line_type == LineType.ADDED:
262
+ result.append(line.content, style=f"{color} on #1a2e1a")
263
+ elif line.line_type == LineType.REMOVED:
264
+ result.append(line.content, style=f"{color} on #2e1a1a")
265
+ elif line.line_type == LineType.ISSUE:
266
+ result.append(line.content, style=f"bold {color}")
267
+ else:
268
+ result.append(line.content, style="#e2e8f0")
269
+
270
+ # Annotation
271
+ if line.annotation:
272
+ padding = max(0, 50 - len(line.content))
273
+ result.append(" " * padding)
274
+ result.append(f" # {line.annotation}", style=f"italic {color}")
275
+
276
+ return result
277
+
278
+ def render(self) -> RenderableType:
279
+ """Render the code context."""
280
+ content = Text()
281
+
282
+ if not self._context:
283
+ content.append("\n No code context set\n", style="#6b7280")
284
+ return Panel(
285
+ content,
286
+ title=f"[bold #3b82f6]{self.title}[/]",
287
+ border_style="#3f3f46",
288
+ )
289
+
290
+ ctx = self._context
291
+
292
+ # Issue header
293
+ content.append(" Issue: ", style="#6b7280")
294
+ content.append(ctx.issue_title, style="bold #eab308")
295
+ content.append("\n\n")
296
+
297
+ # Code lines
298
+ if ctx.lines:
299
+ max_num_width = len(str(max(line.number for line in ctx.lines)))
300
+
301
+ for line in ctx.lines:
302
+ content.append(self._render_line(line, max_num_width))
303
+ content.append("\n")
304
+ else:
305
+ content.append(" No code to display\n", style="#6b7280")
306
+
307
+ # Related findings section
308
+ if ctx.related_findings:
309
+ content.append("\n")
310
+
311
+ if self.show_related:
312
+ content.append(" ▼ Related: ", style="#6b7280")
313
+ content.append(
314
+ f"{len(ctx.related_findings)} similar patterns found\n", style="#a1a1aa"
315
+ )
316
+
317
+ for finding in ctx.related_findings[:5]: # Limit to 5
318
+ content.append(" • ", style="#3f3f46")
319
+ content.append(f"{finding.file_path}", style="#3b82f6")
320
+ content.append(f":{finding.line_number}", style="#6b7280")
321
+ content.append(f" - {finding.description}\n", style="#a1a1aa")
322
+ else:
323
+ content.append(" ▶ Related: ", style="#6b7280")
324
+ content.append(
325
+ f"{len(ctx.related_findings)} similar patterns found", style="#a1a1aa"
326
+ )
327
+ content.append(" (expand to view)\n", style="#52525b")
328
+
329
+ # Build title with file path
330
+ title_text = f"{self.title}: {ctx.file_path}"
331
+ if len(title_text) > 50:
332
+ title_text = f"{self.title}: ...{ctx.file_path[-40:]}"
333
+
334
+ return Panel(
335
+ content,
336
+ title=f"[bold #3b82f6]{title_text}[/]",
337
+ border_style="#3f3f46",
338
+ padding=(0, 0),
339
+ )
340
+
341
+
342
+ class CompactCodeContext(CodeContextViewer):
343
+ """Compact version of CodeContextViewer for smaller displays."""
344
+
345
+ DEFAULT_CSS = """
346
+ CompactCodeContext {
347
+ height: auto;
348
+ max-height: 12;
349
+ border: solid #3f3f46;
350
+ padding: 0 1;
351
+ }
352
+ """
353
+
354
+ def __init__(self, **kwargs):
355
+ kwargs.setdefault("context_lines", 2)
356
+ super().__init__(**kwargs)