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,471 @@
1
+ """
2
+ SuperQode File Viewer - Beautiful File Content Display
3
+
4
+ A rich file viewer with:
5
+ - Syntax highlighting for 100+ languages
6
+ - Line numbers
7
+ - Search highlighting
8
+ - Scrollable content
9
+ - File info header
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import mimetypes
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+ from typing import Optional, List, Tuple
19
+
20
+ from rich.console import Console
21
+ from rich.panel import Panel
22
+ from rich.syntax import Syntax
23
+ from rich.text import Text
24
+ from rich.table import Table
25
+ from rich.box import ROUNDED, SIMPLE
26
+
27
+
28
+ # Language detection by extension
29
+ LANGUAGE_MAP = {
30
+ # Python
31
+ ".py": "python",
32
+ ".pyw": "python",
33
+ ".pyi": "python",
34
+ ".pyx": "python",
35
+ # JavaScript/TypeScript
36
+ ".js": "javascript",
37
+ ".jsx": "jsx",
38
+ ".ts": "typescript",
39
+ ".tsx": "tsx",
40
+ ".mjs": "javascript",
41
+ ".cjs": "javascript",
42
+ # Web
43
+ ".html": "html",
44
+ ".htm": "html",
45
+ ".css": "css",
46
+ ".scss": "scss",
47
+ ".sass": "sass",
48
+ ".less": "less",
49
+ ".vue": "vue",
50
+ ".svelte": "svelte",
51
+ # Data formats
52
+ ".json": "json",
53
+ ".yaml": "yaml",
54
+ ".yml": "yaml",
55
+ ".toml": "toml",
56
+ ".xml": "xml",
57
+ ".csv": "text",
58
+ # Shell
59
+ ".sh": "bash",
60
+ ".bash": "bash",
61
+ ".zsh": "zsh",
62
+ ".fish": "fish",
63
+ ".ps1": "powershell",
64
+ ".bat": "batch",
65
+ ".cmd": "batch",
66
+ # Systems
67
+ ".c": "c",
68
+ ".h": "c",
69
+ ".cpp": "cpp",
70
+ ".hpp": "cpp",
71
+ ".cc": "cpp",
72
+ ".cxx": "cpp",
73
+ ".rs": "rust",
74
+ ".go": "go",
75
+ ".java": "java",
76
+ ".kt": "kotlin",
77
+ ".kts": "kotlin",
78
+ ".scala": "scala",
79
+ ".swift": "swift",
80
+ # Other languages
81
+ ".rb": "ruby",
82
+ ".php": "php",
83
+ ".pl": "perl",
84
+ ".lua": "lua",
85
+ ".r": "r",
86
+ ".R": "r",
87
+ ".jl": "julia",
88
+ ".ex": "elixir",
89
+ ".exs": "elixir",
90
+ ".erl": "erlang",
91
+ ".hs": "haskell",
92
+ ".ml": "ocaml",
93
+ ".fs": "fsharp",
94
+ ".clj": "clojure",
95
+ ".lisp": "lisp",
96
+ ".scm": "scheme",
97
+ # Config files
98
+ ".ini": "ini",
99
+ ".cfg": "ini",
100
+ ".conf": "ini",
101
+ ".env": "dotenv",
102
+ ".gitignore": "gitignore",
103
+ ".dockerignore": "gitignore",
104
+ ".editorconfig": "ini",
105
+ # Documentation
106
+ ".md": "markdown",
107
+ ".markdown": "markdown",
108
+ ".rst": "rst",
109
+ ".txt": "text",
110
+ ".log": "text",
111
+ # Database
112
+ ".sql": "sql",
113
+ ".sqlite": "sql",
114
+ # Build/Config
115
+ "Makefile": "makefile",
116
+ "Dockerfile": "dockerfile",
117
+ ".dockerfile": "dockerfile",
118
+ "Vagrantfile": "ruby",
119
+ "Gemfile": "ruby",
120
+ "Rakefile": "ruby",
121
+ "CMakeLists.txt": "cmake",
122
+ # Other
123
+ ".diff": "diff",
124
+ ".patch": "diff",
125
+ ".graphql": "graphql",
126
+ ".proto": "protobuf",
127
+ ".tf": "terraform",
128
+ ".hcl": "hcl",
129
+ }
130
+
131
+ # File type icons
132
+ FILE_ICONS = {
133
+ "python": "🐍",
134
+ "javascript": "📜",
135
+ "typescript": "💠",
136
+ "html": "🌐",
137
+ "css": "🎨",
138
+ "json": "📋",
139
+ "yaml": "⚙️",
140
+ "markdown": "📝",
141
+ "rust": "🦀",
142
+ "go": "🐹",
143
+ "java": "☕",
144
+ "ruby": "💎",
145
+ "php": "🐘",
146
+ "bash": "💻",
147
+ "sql": "🗄️",
148
+ "dockerfile": "🐳",
149
+ "default": "📄",
150
+ }
151
+
152
+ # SuperQode viewer colors
153
+ VIEWER_COLORS = {
154
+ "header": "#a855f7",
155
+ "border": "#2a2a2a",
156
+ "line_no": "#52525b",
157
+ "highlight": "#f59e0b30",
158
+ "search": "#22c55e40",
159
+ "info": "#71717a",
160
+ }
161
+
162
+
163
+ @dataclass
164
+ class FileInfo:
165
+ """Information about a file."""
166
+
167
+ path: str
168
+ name: str
169
+ extension: str
170
+ language: str
171
+ size: int
172
+ lines: int
173
+ encoding: str = "utf-8"
174
+ is_binary: bool = False
175
+
176
+
177
+ def detect_language(path: str) -> str:
178
+ """Detect the programming language from file path."""
179
+ p = Path(path)
180
+ name = p.name
181
+ ext = p.suffix.lower()
182
+
183
+ # Check exact filename matches first
184
+ if name in LANGUAGE_MAP:
185
+ return LANGUAGE_MAP[name]
186
+
187
+ # Check extension
188
+ if ext in LANGUAGE_MAP:
189
+ return LANGUAGE_MAP[ext]
190
+
191
+ return "text"
192
+
193
+
194
+ def get_file_icon(language: str) -> str:
195
+ """Get an icon for a language."""
196
+ return FILE_ICONS.get(language, FILE_ICONS["default"])
197
+
198
+
199
+ def get_file_info(path: str) -> FileInfo:
200
+ """Get information about a file."""
201
+ p = Path(path)
202
+
203
+ if not p.exists():
204
+ raise FileNotFoundError(f"File not found: {path}")
205
+
206
+ stat = p.stat()
207
+ ext = p.suffix.lower()
208
+ language = detect_language(path)
209
+
210
+ # Check if binary
211
+ is_binary = False
212
+ try:
213
+ with open(p, "rb") as f:
214
+ chunk = f.read(8192)
215
+ if b"\x00" in chunk:
216
+ is_binary = True
217
+ except Exception:
218
+ pass
219
+
220
+ # Count lines
221
+ lines = 0
222
+ if not is_binary:
223
+ try:
224
+ with open(p, "r", encoding="utf-8") as f:
225
+ lines = sum(1 for _ in f)
226
+ except Exception:
227
+ pass
228
+
229
+ return FileInfo(
230
+ path=str(p.resolve()),
231
+ name=p.name,
232
+ extension=ext,
233
+ language=language,
234
+ size=stat.st_size,
235
+ lines=lines,
236
+ is_binary=is_binary,
237
+ )
238
+
239
+
240
+ def format_size(size: int) -> str:
241
+ """Format file size in human-readable format."""
242
+ for unit in ["B", "KB", "MB", "GB"]:
243
+ if size < 1024:
244
+ return f"{size:.1f} {unit}" if unit != "B" else f"{size} {unit}"
245
+ size /= 1024
246
+ return f"{size:.1f} TB"
247
+
248
+
249
+ class FileViewer:
250
+ """Interactive file viewer with syntax highlighting."""
251
+
252
+ def __init__(self, console: Console):
253
+ self.console = console
254
+ self.current_file: Optional[str] = None
255
+ self.content: Optional[str] = None
256
+ self.info: Optional[FileInfo] = None
257
+ self.scroll_offset: int = 0
258
+ self.search_term: Optional[str] = None
259
+ self.highlight_lines: List[int] = []
260
+
261
+ def open(self, path: str) -> bool:
262
+ """Open a file for viewing."""
263
+ try:
264
+ self.info = get_file_info(path)
265
+
266
+ if self.info.is_binary:
267
+ self.content = None
268
+ return True
269
+
270
+ with open(path, "r", encoding="utf-8") as f:
271
+ self.content = f.read()
272
+
273
+ self.current_file = path
274
+ self.scroll_offset = 0
275
+ self.search_term = None
276
+ self.highlight_lines = []
277
+ return True
278
+
279
+ except Exception as e:
280
+ self.console.print(f" [red]Error opening file: {e}[/red]")
281
+ return False
282
+
283
+ def render(
284
+ self,
285
+ start_line: int = 1,
286
+ end_line: Optional[int] = None,
287
+ show_header: bool = True,
288
+ theme: str = "monokai",
289
+ ) -> None:
290
+ """Render the file content."""
291
+ if self.info is None:
292
+ self.console.print(" [dim]No file open[/dim]")
293
+ return
294
+
295
+ if show_header:
296
+ self._render_header()
297
+
298
+ if self.info.is_binary:
299
+ self.console.print(" [dim]Binary file - cannot display content[/dim]")
300
+ return
301
+
302
+ if self.content is None:
303
+ self.console.print(" [dim]No content to display[/dim]")
304
+ return
305
+
306
+ # Get content slice
307
+ lines = self.content.splitlines()
308
+ total_lines = len(lines)
309
+
310
+ if end_line is None:
311
+ end_line = min(start_line + 50, total_lines) # Default 50 lines
312
+
313
+ start_idx = max(0, start_line - 1)
314
+ end_idx = min(end_line, total_lines)
315
+
316
+ content_slice = "\n".join(lines[start_idx:end_idx])
317
+
318
+ # Create syntax highlighted view
319
+ syntax = Syntax(
320
+ content_slice,
321
+ self.info.language,
322
+ theme=theme,
323
+ line_numbers=True,
324
+ start_line=start_line,
325
+ word_wrap=True,
326
+ highlight_lines=set(self.highlight_lines) if self.highlight_lines else None,
327
+ background_color="#000000",
328
+ )
329
+
330
+ self.console.print(
331
+ Panel(syntax, border_style=VIEWER_COLORS["border"], box=ROUNDED, padding=(0, 1))
332
+ )
333
+
334
+ # Show position info
335
+ if total_lines > end_idx:
336
+ self.console.print(
337
+ f" [dim]Showing lines {start_line}-{end_idx} of {total_lines}[/dim]"
338
+ )
339
+
340
+ def _render_header(self) -> None:
341
+ """Render the file header."""
342
+ if self.info is None:
343
+ return
344
+
345
+ icon = get_file_icon(self.info.language)
346
+
347
+ header = Text()
348
+ header.append(f" {icon} ", style="bold")
349
+ header.append(self.info.name, style="bold white")
350
+ header.append(" ", style="")
351
+ header.append(f"[{self.info.language}]", style="bold cyan")
352
+ header.append(" ", style="")
353
+ header.append(format_size(self.info.size), style="dim")
354
+ header.append(" ", style="")
355
+ header.append(f"{self.info.lines} lines", style="dim")
356
+
357
+ self.console.print(
358
+ Panel(header, border_style=VIEWER_COLORS["header"], box=ROUNDED, padding=(0, 1))
359
+ )
360
+
361
+ def search(self, term: str) -> List[Tuple[int, str]]:
362
+ """Search for a term in the file content."""
363
+ if self.content is None:
364
+ return []
365
+
366
+ self.search_term = term
367
+ results = []
368
+ self.highlight_lines = []
369
+
370
+ for i, line in enumerate(self.content.splitlines(), 1):
371
+ if term.lower() in line.lower():
372
+ results.append((i, line.strip()))
373
+ self.highlight_lines.append(i)
374
+
375
+ return results
376
+
377
+ def goto_line(self, line: int) -> None:
378
+ """Jump to a specific line."""
379
+ if self.content is None:
380
+ return
381
+
382
+ total_lines = len(self.content.splitlines())
383
+ line = max(1, min(line, total_lines))
384
+ self.scroll_offset = line - 1
385
+ self.render(start_line=line)
386
+
387
+ def render_search_results(self, results: List[Tuple[int, str]], max_results: int = 10) -> None:
388
+ """Render search results."""
389
+ if not results:
390
+ self.console.print(f" [dim]No matches found for '{self.search_term}'[/dim]")
391
+ return
392
+
393
+ header = Text()
394
+ header.append(f" 🔍 ", style="bold")
395
+ header.append(f"{len(results)} match(es)", style="bold white")
396
+ header.append(f" for '{self.search_term}'", style="dim")
397
+
398
+ self.console.print(Panel(header, border_style="#06b6d4", box=ROUNDED, padding=(0, 1)))
399
+
400
+ for line_no, content in results[:max_results]:
401
+ line = Text()
402
+ line.append(f" {line_no:>4}: ", style=VIEWER_COLORS["line_no"])
403
+
404
+ # Highlight search term
405
+ content_lower = content.lower()
406
+ term_lower = self.search_term.lower() if self.search_term else ""
407
+
408
+ if term_lower in content_lower:
409
+ idx = content_lower.index(term_lower)
410
+ line.append(content[:idx], style="")
411
+ line.append(content[idx : idx + len(term_lower)], style="bold yellow on #f59e0b30")
412
+ line.append(content[idx + len(term_lower) :], style="")
413
+ else:
414
+ line.append(content, style="")
415
+
416
+ self.console.print(line)
417
+
418
+ if len(results) > max_results:
419
+ self.console.print(f" [dim]... and {len(results) - max_results} more[/dim]")
420
+
421
+
422
+ def render_file(
423
+ console: Console,
424
+ path: str,
425
+ start_line: int = 1,
426
+ end_line: Optional[int] = None,
427
+ theme: str = "monokai",
428
+ ) -> None:
429
+ """Render a file with syntax highlighting (simple interface)."""
430
+ viewer = FileViewer(console)
431
+ if viewer.open(path):
432
+ viewer.render(start_line, end_line, theme=theme)
433
+
434
+
435
+ def render_file_preview(
436
+ console: Console, path: str, max_lines: int = 20, theme: str = "monokai"
437
+ ) -> None:
438
+ """Render a preview of a file (first N lines)."""
439
+ viewer = FileViewer(console)
440
+ if viewer.open(path):
441
+ viewer.render(1, max_lines, theme=theme)
442
+
443
+
444
+ def render_file_info(console: Console, path: str) -> None:
445
+ """Render file information without content."""
446
+ try:
447
+ info = get_file_info(path)
448
+ icon = get_file_icon(info.language)
449
+
450
+ table = Table(show_header=False, box=SIMPLE, padding=(0, 2))
451
+ table.add_column("Property", style="dim")
452
+ table.add_column("Value", style="white")
453
+
454
+ table.add_row("Name", f"{icon} {info.name}")
455
+ table.add_row("Path", info.path)
456
+ table.add_row("Language", info.language)
457
+ table.add_row("Size", format_size(info.size))
458
+ table.add_row("Lines", str(info.lines))
459
+ table.add_row("Binary", "Yes" if info.is_binary else "No")
460
+
461
+ console.print(
462
+ Panel(
463
+ table,
464
+ title="[bold]File Info[/bold]",
465
+ border_style=VIEWER_COLORS["header"],
466
+ box=ROUNDED,
467
+ )
468
+ )
469
+
470
+ except Exception as e:
471
+ console.print(f" [red]Error: {e}[/red]")
superqode/flash.py ADDED
@@ -0,0 +1,183 @@
1
+ """
2
+ SuperQode Flash Notifications - Temporary Status Messages
3
+
4
+ Beautiful flash notifications with:
5
+ - Semantic styles (success, warning, error, info)
6
+ - Auto-hide with configurable duration
7
+ - Gradient animations
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+ from enum import Enum
14
+ from typing import Optional, Callable
15
+ from datetime import datetime
16
+ import threading
17
+ import time
18
+
19
+ from rich.console import Console
20
+ from rich.text import Text
21
+ from rich.panel import Panel
22
+ from rich.box import ROUNDED
23
+
24
+
25
+ class FlashStyle(Enum):
26
+ """Flash notification styles."""
27
+
28
+ DEFAULT = "default"
29
+ SUCCESS = "success"
30
+ WARNING = "warning"
31
+ ERROR = "error"
32
+ INFO = "info"
33
+
34
+
35
+ @dataclass
36
+ class FlashMessage:
37
+ """A flash notification message."""
38
+
39
+ content: str
40
+ style: FlashStyle = FlashStyle.DEFAULT
41
+ duration: float = 3.0
42
+ created_at: datetime = field(default_factory=datetime.now)
43
+ dismissed: bool = False
44
+
45
+
46
+ # SuperQode flash colors
47
+ FLASH_COLORS = {
48
+ FlashStyle.DEFAULT: {
49
+ "icon": "💬",
50
+ "color": "#a855f7",
51
+ "bg": "#a855f720",
52
+ "border": "#a855f7",
53
+ },
54
+ FlashStyle.SUCCESS: {
55
+ "icon": "✅",
56
+ "color": "#22c55e",
57
+ "bg": "#22c55e20",
58
+ "border": "#22c55e",
59
+ },
60
+ FlashStyle.WARNING: {
61
+ "icon": "⚠️",
62
+ "color": "#f59e0b",
63
+ "bg": "#f59e0b20",
64
+ "border": "#f59e0b",
65
+ },
66
+ FlashStyle.ERROR: {
67
+ "icon": "❌",
68
+ "color": "#ef4444",
69
+ "bg": "#ef444420",
70
+ "border": "#ef4444",
71
+ },
72
+ FlashStyle.INFO: {
73
+ "icon": "ℹ️",
74
+ "color": "#06b6d4",
75
+ "bg": "#06b6d420",
76
+ "border": "#06b6d4",
77
+ },
78
+ }
79
+
80
+
81
+ class FlashManager:
82
+ """Manages flash notifications."""
83
+
84
+ def __init__(self, console: Console, default_duration: float = 3.0):
85
+ self.console = console
86
+ self.default_duration = default_duration
87
+ self.messages: list[FlashMessage] = []
88
+ self.history: list[FlashMessage] = []
89
+ self._timer: Optional[threading.Timer] = None
90
+
91
+ def flash(
92
+ self, content: str, style: FlashStyle = FlashStyle.DEFAULT, duration: Optional[float] = None
93
+ ) -> FlashMessage:
94
+ """Show a flash notification."""
95
+ msg = FlashMessage(content=content, style=style, duration=duration or self.default_duration)
96
+ self.messages.append(msg)
97
+ self._render_flash(msg)
98
+
99
+ # Schedule auto-dismiss
100
+ if msg.duration > 0:
101
+ self._schedule_dismiss(msg)
102
+
103
+ return msg
104
+
105
+ def success(self, content: str, duration: Optional[float] = None) -> FlashMessage:
106
+ """Show a success flash."""
107
+ return self.flash(content, FlashStyle.SUCCESS, duration)
108
+
109
+ def warning(self, content: str, duration: Optional[float] = None) -> FlashMessage:
110
+ """Show a warning flash."""
111
+ return self.flash(content, FlashStyle.WARNING, duration)
112
+
113
+ def error(self, content: str, duration: Optional[float] = None) -> FlashMessage:
114
+ """Show an error flash."""
115
+ return self.flash(content, FlashStyle.ERROR, duration)
116
+
117
+ def info(self, content: str, duration: Optional[float] = None) -> FlashMessage:
118
+ """Show an info flash."""
119
+ return self.flash(content, FlashStyle.INFO, duration)
120
+
121
+ def dismiss(self, msg: FlashMessage) -> None:
122
+ """Dismiss a flash message."""
123
+ msg.dismissed = True
124
+ if msg in self.messages:
125
+ self.messages.remove(msg)
126
+ self.history.append(msg)
127
+
128
+ def dismiss_all(self) -> None:
129
+ """Dismiss all flash messages."""
130
+ for msg in list(self.messages):
131
+ self.dismiss(msg)
132
+
133
+ def _schedule_dismiss(self, msg: FlashMessage) -> None:
134
+ """Schedule auto-dismiss of a message."""
135
+
136
+ def auto_dismiss():
137
+ if not msg.dismissed:
138
+ self.dismiss(msg)
139
+
140
+ timer = threading.Timer(msg.duration, auto_dismiss)
141
+ timer.daemon = True
142
+ timer.start()
143
+
144
+ def _render_flash(self, msg: FlashMessage) -> None:
145
+ """Render a flash message."""
146
+ style_config = FLASH_COLORS.get(msg.style, FLASH_COLORS[FlashStyle.DEFAULT])
147
+
148
+ text = Text()
149
+ text.append(f" {style_config['icon']} ", style=f"bold {style_config['color']}")
150
+ text.append(msg.content, style=style_config["color"])
151
+
152
+ self.console.print(text)
153
+
154
+
155
+ def render_flash(console: Console, content: str, style: FlashStyle = FlashStyle.DEFAULT) -> None:
156
+ """Render a simple flash message (stateless)."""
157
+ style_config = FLASH_COLORS.get(style, FLASH_COLORS[FlashStyle.DEFAULT])
158
+
159
+ text = Text()
160
+ text.append(f" {style_config['icon']} ", style=f"bold {style_config['color']}")
161
+ text.append(content, style=style_config["color"])
162
+
163
+ console.print(text)
164
+
165
+
166
+ def flash_success(console: Console, content: str) -> None:
167
+ """Show a success flash."""
168
+ render_flash(console, content, FlashStyle.SUCCESS)
169
+
170
+
171
+ def flash_warning(console: Console, content: str) -> None:
172
+ """Show a warning flash."""
173
+ render_flash(console, content, FlashStyle.WARNING)
174
+
175
+
176
+ def flash_error(console: Console, content: str) -> None:
177
+ """Show an error flash."""
178
+ render_flash(console, content, FlashStyle.ERROR)
179
+
180
+
181
+ def flash_info(console: Console, content: str) -> None:
182
+ """Show an info flash."""
183
+ render_flash(console, content, FlashStyle.INFO)
@@ -0,0 +1,58 @@
1
+ """
2
+ SuperQode QE Guidance - System prompts for time-constrained QE.
3
+
4
+ Provides verification-first guidance for QE agents to prevent:
5
+ - False positives (claiming success without proof)
6
+ - Shallow testing (skipping edge cases)
7
+ - Time wasting (long speculative work before feedback)
8
+
9
+ Configuration is driven entirely by superqode.yaml:
10
+
11
+ ```yaml
12
+ superqode:
13
+ qe:
14
+ guidance:
15
+ enabled: true
16
+
17
+ # Mode-specific timeouts
18
+ quick_scan:
19
+ timeout_seconds: 60
20
+ verification_first: true
21
+ fail_fast: true
22
+
23
+ deep_qe:
24
+ timeout_seconds: 1800
25
+ exploration_allowed: true
26
+ destructive_testing: true
27
+
28
+ # Anti-patterns to detect
29
+ anti_patterns:
30
+ - skip_verification
31
+ - unconditional_success
32
+ - broad_exception_swallow
33
+ - weaken_tests
34
+ ```
35
+
36
+ Integrates with:
37
+ - CI: `superqe run --mode quick` (proactive)
38
+ - TUI: Interactive QE sessions (reactive)
39
+ """
40
+
41
+ from .prompts import (
42
+ QEGuidance,
43
+ GuidanceMode,
44
+ get_qe_system_prompt,
45
+ get_qe_review_prompt,
46
+ get_qe_goal_suffix,
47
+ )
48
+ from .config import GuidanceConfig, load_guidance_config
49
+
50
+ __all__ = [
51
+ "QEGuidance",
52
+ "GuidanceMode",
53
+ "get_qe_system_prompt",
54
+ "get_qe_review_prompt",
55
+ "get_qe_goal_suffix",
56
+ "GuidanceConfig",
57
+ "load_guidance_config",
58
+ ]