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,786 @@
1
+ """
2
+ SuperQode Display Widgets - Enhanced Display Components.
3
+
4
+ Provides enhanced display widgets for the main app that use
5
+ SuperQode's unique design system. Drop-in replacements for
6
+ existing display functions.
7
+
8
+ Usage in app_main.py:
9
+ from superqode.widgets.superqode_display import (
10
+ EnhancedToolPanel, EnhancedThinkingBar, EnhancedResponsePanel,
11
+ EnhancedStatusHeader, show_tool_call, show_thinking, show_response,
12
+ )
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from dataclasses import dataclass, field
18
+ from datetime import datetime
19
+ from enum import Enum, auto
20
+ from pathlib import Path
21
+ from time import monotonic
22
+ from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING
23
+
24
+ from rich.text import Text
25
+ from rich.panel import Panel
26
+ from rich.syntax import Syntax
27
+ from rich.console import Group
28
+ from rich.markdown import Markdown
29
+
30
+ from textual.widgets import Static, RichLog
31
+ from textual.containers import Container, Horizontal, Vertical, ScrollableContainer
32
+ from textual.reactive import reactive
33
+ from textual.timer import Timer
34
+
35
+ if TYPE_CHECKING:
36
+ from textual.app import App
37
+
38
+
39
+ # ============================================================================
40
+ # DESIGN SYSTEM IMPORTS
41
+ # ============================================================================
42
+
43
+ try:
44
+ from superqode.design_system import (
45
+ COLORS,
46
+ GRADIENT_PURPLE,
47
+ GRADIENT_QUANTUM,
48
+ SUPERQODE_ICONS,
49
+ BORDER_CHARS,
50
+ render_gradient_text,
51
+ render_status_indicator,
52
+ render_tool_indicator,
53
+ render_thinking_line,
54
+ render_message_header,
55
+ get_animation_frame,
56
+ )
57
+ except ImportError:
58
+ # Fallback colors
59
+ class COLORS:
60
+ primary = "#7c3aed"
61
+ primary_light = "#a855f7"
62
+ secondary = "#ec4899"
63
+ success = "#10b981"
64
+ error = "#f43f5e"
65
+ warning = "#f59e0b"
66
+ info = "#06b6d4"
67
+ text_primary = "#fafafa"
68
+ text_secondary = "#e4e4e7"
69
+ text_muted = "#a1a1aa"
70
+ text_dim = "#71717a"
71
+ text_ghost = "#52525b"
72
+ bg_surface = "#050505"
73
+ border_subtle = "#1a1a1a"
74
+
75
+ GRADIENT_PURPLE = ["#6d28d9", "#7c3aed", "#8b5cf6", "#a855f7"]
76
+ SUPERQODE_ICONS = {"thinking": "◈", "success": "✦", "error": "✕"}
77
+
78
+
79
+ # ============================================================================
80
+ # ENUMS
81
+ # ============================================================================
82
+
83
+
84
+ class ToolStatus(Enum):
85
+ """Tool execution status."""
86
+
87
+ PENDING = auto()
88
+ RUNNING = auto()
89
+ SUCCESS = auto()
90
+ ERROR = auto()
91
+
92
+
93
+ class AgentState(Enum):
94
+ """Agent execution state."""
95
+
96
+ IDLE = auto()
97
+ THINKING = auto()
98
+ STREAMING = auto()
99
+ TOOL_CALL = auto()
100
+ WAITING = auto()
101
+
102
+
103
+ # ============================================================================
104
+ # DATA CLASSES
105
+ # ============================================================================
106
+
107
+
108
+ @dataclass
109
+ class ToolCallInfo:
110
+ """Information about a tool call."""
111
+
112
+ id: str
113
+ name: str
114
+ kind: str = "other"
115
+ status: ToolStatus = ToolStatus.PENDING
116
+ start_time: Optional[datetime] = None
117
+ end_time: Optional[datetime] = None
118
+ arguments: Dict[str, Any] = field(default_factory=dict)
119
+ result: str = ""
120
+ error: str = ""
121
+ file_path: str = ""
122
+
123
+
124
+ @dataclass
125
+ class SessionStats:
126
+ """Session statistics."""
127
+
128
+ message_count: int = 0
129
+ tool_count: int = 0
130
+ prompt_tokens: int = 0
131
+ completion_tokens: int = 0
132
+ total_cost: float = 0.0
133
+ files_read: List[str] = field(default_factory=list)
134
+ files_modified: List[str] = field(default_factory=list)
135
+
136
+
137
+ # ============================================================================
138
+ # ENHANCED STATUS HEADER
139
+ # ============================================================================
140
+
141
+
142
+ class EnhancedStatusHeader(Static):
143
+ """
144
+ Enhanced status header showing connection, model, and session info.
145
+
146
+ SuperQode style: Clean, minimal, informative.
147
+ """
148
+
149
+ DEFAULT_CSS = """
150
+ EnhancedStatusHeader {
151
+ height: 1;
152
+ background: #0a0a0a;
153
+ border-bottom: solid #1a1a1a;
154
+ padding: 0 1;
155
+ }
156
+ """
157
+
158
+ # Reactive state
159
+ connected: reactive[bool] = reactive(False)
160
+ agent_name: reactive[str] = reactive("")
161
+ model_name: reactive[str] = reactive("")
162
+ connection_type: reactive[str] = reactive("")
163
+ state: reactive[AgentState] = reactive(AgentState.IDLE)
164
+ approval_mode: reactive[str] = reactive("ask")
165
+
166
+ def __init__(self, **kwargs):
167
+ super().__init__("", **kwargs)
168
+ self._stats = SessionStats()
169
+ self._animation_tick = 0
170
+ self._timer: Optional[Timer] = None
171
+
172
+ def on_mount(self) -> None:
173
+ """Start animation timer."""
174
+ self._timer = self.set_interval(0.5, self._animate)
175
+
176
+ def _animate(self) -> None:
177
+ """Animation tick."""
178
+ self._animation_tick += 1
179
+ if self.state in (AgentState.THINKING, AgentState.STREAMING, AgentState.TOOL_CALL):
180
+ self.refresh()
181
+
182
+ def update_stats(self, **kwargs) -> None:
183
+ """Update session statistics."""
184
+ for key, value in kwargs.items():
185
+ if hasattr(self._stats, key):
186
+ setattr(self._stats, key, value)
187
+ self.refresh()
188
+
189
+ def render(self) -> Text:
190
+ """Render the status header."""
191
+ text = Text()
192
+
193
+ # Connection indicator
194
+ if self.connected:
195
+ text.append("● ", style=f"bold {COLORS.success}")
196
+ if self.agent_name:
197
+ text.append(self.agent_name, style=f"bold {COLORS.text_secondary}")
198
+ if self.model_name:
199
+ text.append(" → ", style=COLORS.text_dim)
200
+ text.append(self.model_name, style=COLORS.text_muted)
201
+ if self.connection_type:
202
+ conn_colors = {"acp": COLORS.success, "byok": COLORS.info, "local": COLORS.warning}
203
+ text.append(
204
+ f" [{self.connection_type.upper()}]",
205
+ style=conn_colors.get(self.connection_type, COLORS.text_muted),
206
+ )
207
+ else:
208
+ text.append("○ ", style=COLORS.text_dim)
209
+ text.append("Not connected", style=COLORS.text_dim)
210
+
211
+ text.append(" │ ", style=COLORS.text_ghost)
212
+
213
+ # State indicator with animation
214
+ state_text = {
215
+ AgentState.IDLE: ("◇", "Idle", COLORS.text_dim),
216
+ AgentState.THINKING: ("◈", "Thinking", COLORS.primary_light),
217
+ AgentState.STREAMING: ("▸", "Streaming", COLORS.secondary),
218
+ AgentState.TOOL_CALL: ("⚡", "Running", COLORS.warning),
219
+ AgentState.WAITING: ("⏸", "Waiting", COLORS.info),
220
+ }
221
+ icon, label, color = state_text.get(self.state, ("◇", "Idle", COLORS.text_dim))
222
+
223
+ # Animate icon for active states
224
+ if self.state in (AgentState.THINKING, AgentState.STREAMING, AgentState.TOOL_CALL):
225
+ frames = ["◇", "◆", "◈", "◆"]
226
+ icon = frames[self._animation_tick % len(frames)]
227
+
228
+ text.append(f"{icon} ", style=f"bold {color}")
229
+ text.append(label, style=color)
230
+
231
+ text.append(" │ ", style=COLORS.text_ghost)
232
+
233
+ # Approval mode
234
+ mode_styles = {
235
+ "auto": ("●", "AUTO", COLORS.success),
236
+ "ask": ("●", "ASK", COLORS.warning),
237
+ "deny": ("●", "DENY", COLORS.error),
238
+ }
239
+ m_icon, m_label, m_color = mode_styles.get(self.approval_mode, ("●", "ASK", COLORS.warning))
240
+ text.append(f"{m_icon} ", style=m_color)
241
+ text.append(m_label, style=f"bold {m_color}")
242
+
243
+ # Stats
244
+ if self._stats.tool_count > 0 or self._stats.message_count > 0:
245
+ text.append(" │ ", style=COLORS.text_ghost)
246
+ text.append(f"{self._stats.message_count} msgs", style=COLORS.text_dim)
247
+ if self._stats.tool_count > 0:
248
+ text.append(f" · {self._stats.tool_count} tools", style=COLORS.text_dim)
249
+
250
+ # Tokens/cost
251
+ total_tokens = self._stats.prompt_tokens + self._stats.completion_tokens
252
+ if total_tokens > 0:
253
+ text.append(" │ ", style=COLORS.text_ghost)
254
+ text.append(f"{total_tokens:,} tokens", style=COLORS.text_dim)
255
+ if self._stats.total_cost > 0:
256
+ text.append(f" · ${self._stats.total_cost:.4f}", style=COLORS.text_dim)
257
+
258
+ return text
259
+
260
+
261
+ # ============================================================================
262
+ # ENHANCED TOOL PANEL
263
+ # ============================================================================
264
+
265
+
266
+ class EnhancedToolPanel(Container):
267
+ """
268
+ Enhanced tool panel showing tool calls with minimal, clean styling.
269
+
270
+ SuperQode style: Left-border indicators, compact layout.
271
+ """
272
+
273
+ DEFAULT_CSS = """
274
+ EnhancedToolPanel {
275
+ height: auto;
276
+ max-height: 30%;
277
+ background: #050505;
278
+ border-bottom: solid #1a1a1a;
279
+ padding: 0 1;
280
+ }
281
+
282
+ EnhancedToolPanel.collapsed {
283
+ max-height: 1;
284
+ overflow: hidden;
285
+ }
286
+
287
+ EnhancedToolPanel .tool-header {
288
+ height: 1;
289
+ color: #71717a;
290
+ }
291
+
292
+ EnhancedToolPanel .tool-list {
293
+ height: auto;
294
+ max-height: 100%;
295
+ }
296
+ """
297
+
298
+ collapsed: reactive[bool] = reactive(True)
299
+
300
+ def __init__(self, **kwargs):
301
+ super().__init__(**kwargs)
302
+ self._tools: Dict[str, ToolCallInfo] = {}
303
+ self._tool_order: List[str] = []
304
+
305
+ def compose(self):
306
+ """Compose the tool panel."""
307
+ yield Static(self._render_header(), id="tool-header", classes="tool-header")
308
+ yield Static("", id="tool-list", classes="tool-list")
309
+
310
+ def _render_header(self) -> Text:
311
+ """Render the header."""
312
+ text = Text()
313
+ icon = "▾" if not self.collapsed else "▸"
314
+ text.append(f"{icon} ", style=COLORS.text_dim)
315
+ text.append("Tools", style=COLORS.text_muted)
316
+
317
+ running = sum(1 for t in self._tools.values() if t.status == ToolStatus.RUNNING)
318
+ if running > 0:
319
+ text.append(f" ({running} running)", style=f"bold {COLORS.warning}")
320
+ elif len(self._tools) > 0:
321
+ text.append(f" ({len(self._tools)} total)", style=COLORS.text_dim)
322
+
323
+ return text
324
+
325
+ def _render_tools(self) -> Text:
326
+ """Render the tool list."""
327
+ if self.collapsed:
328
+ return Text()
329
+
330
+ text = Text()
331
+
332
+ # Show last 5 tools
333
+ for tool_id in self._tool_order[-5:]:
334
+ tool = self._tools.get(tool_id)
335
+ if not tool:
336
+ continue
337
+
338
+ # Status icon and color
339
+ status_map = {
340
+ ToolStatus.PENDING: ("○", COLORS.text_dim),
341
+ ToolStatus.RUNNING: ("◐", COLORS.primary_light),
342
+ ToolStatus.SUCCESS: ("✦", COLORS.success),
343
+ ToolStatus.ERROR: ("✕", COLORS.error),
344
+ }
345
+ icon, color = status_map.get(tool.status, ("•", COLORS.text_dim))
346
+
347
+ # Tool kind icon
348
+ kind_icons = {
349
+ "read": "↳",
350
+ "write": "↲",
351
+ "edit": "⟳",
352
+ "shell": "▸",
353
+ "search": "⌕",
354
+ "glob": "⋮",
355
+ }
356
+ kind_icon = kind_icons.get(tool.kind.lower(), "•")
357
+
358
+ text.append(f" {icon} ", style=f"bold {color}")
359
+ text.append(f"{kind_icon} ", style=COLORS.text_dim)
360
+ text.append(tool.name, style=COLORS.text_secondary)
361
+
362
+ if tool.file_path:
363
+ text.append(f" {tool.file_path}", style=COLORS.text_ghost)
364
+
365
+ # Duration for completed tools
366
+ if (
367
+ tool.status in (ToolStatus.SUCCESS, ToolStatus.ERROR)
368
+ and tool.end_time
369
+ and tool.start_time
370
+ ):
371
+ duration = (tool.end_time - tool.start_time).total_seconds()
372
+ text.append(f" ({duration:.1f}s)", style=COLORS.text_ghost)
373
+
374
+ text.append("\n")
375
+
376
+ return text
377
+
378
+ def add_tool(
379
+ self, tool_id: str, name: str, kind: str = "other", arguments: Dict[str, Any] = None
380
+ ) -> None:
381
+ """Add a new tool call."""
382
+ file_path = ""
383
+ if arguments:
384
+ file_path = arguments.get(
385
+ "path", arguments.get("file_path", arguments.get("filePath", ""))
386
+ )
387
+
388
+ tool = ToolCallInfo(
389
+ id=tool_id,
390
+ name=name,
391
+ kind=kind,
392
+ status=ToolStatus.RUNNING,
393
+ start_time=datetime.now(),
394
+ arguments=arguments or {},
395
+ file_path=file_path,
396
+ )
397
+
398
+ self._tools[tool_id] = tool
399
+ if tool_id not in self._tool_order:
400
+ self._tool_order.append(tool_id)
401
+
402
+ # Auto-expand when tools are running
403
+ self.collapsed = False
404
+ self._refresh()
405
+
406
+ def complete_tool(self, tool_id: str, result: str = "", error: str = "") -> None:
407
+ """Mark a tool as complete."""
408
+ tool = self._tools.get(tool_id)
409
+ if tool:
410
+ tool.end_time = datetime.now()
411
+ tool.status = ToolStatus.ERROR if error else ToolStatus.SUCCESS
412
+ tool.result = result
413
+ tool.error = error
414
+ self._refresh()
415
+
416
+ def clear(self) -> None:
417
+ """Clear all tools."""
418
+ self._tools.clear()
419
+ self._tool_order.clear()
420
+ self._refresh()
421
+
422
+ def _refresh(self) -> None:
423
+ """Refresh the display."""
424
+ try:
425
+ self.query_one("#tool-header", Static).update(self._render_header())
426
+ self.query_one("#tool-list", Static).update(self._render_tools())
427
+ except Exception:
428
+ pass
429
+
430
+
431
+ # ============================================================================
432
+ # ENHANCED THINKING BAR
433
+ # ============================================================================
434
+
435
+
436
+ class EnhancedThinkingBar(Static):
437
+ """
438
+ Enhanced thinking indicator with subtle animation.
439
+
440
+ SuperQode style: Minimal, one-line, quantum-inspired animation.
441
+ """
442
+
443
+ DEFAULT_CSS = """
444
+ EnhancedThinkingBar {
445
+ height: 1;
446
+ background: #050505;
447
+ padding: 0 1;
448
+ display: none;
449
+ }
450
+
451
+ EnhancedThinkingBar.visible {
452
+ display: block;
453
+ }
454
+ """
455
+
456
+ visible: reactive[bool] = reactive(False)
457
+
458
+ def __init__(self, **kwargs):
459
+ super().__init__("", **kwargs)
460
+ self._text = ""
461
+ self._tick = 0
462
+ self._timer: Optional[Timer] = None
463
+
464
+ def watch_visible(self, visible: bool) -> None:
465
+ """Handle visibility change."""
466
+ if visible:
467
+ self.add_class("visible")
468
+ self._timer = self.set_interval(0.15, self._animate)
469
+ else:
470
+ self.remove_class("visible")
471
+ if self._timer:
472
+ self._timer.stop()
473
+ self._timer = None
474
+
475
+ def _animate(self) -> None:
476
+ """Animation tick."""
477
+ self._tick += 1
478
+ self.refresh()
479
+
480
+ def set_text(self, text: str) -> None:
481
+ """Set the thinking text."""
482
+ self._text = text
483
+ self.refresh()
484
+
485
+ def render(self) -> Text:
486
+ """Render the thinking bar."""
487
+ result = Text()
488
+
489
+ # Animated quantum dots
490
+ frames = [
491
+ "◇ ◇ ◇",
492
+ "◆ ◇ ◇",
493
+ "◇ ◆ ◇",
494
+ "◇ ◇ ◆",
495
+ "◇ ◆ ◇",
496
+ "◆ ◇ ◇",
497
+ ]
498
+ frame = frames[self._tick % len(frames)]
499
+
500
+ # Color cycling
501
+ colors = GRADIENT_PURPLE
502
+ color = colors[self._tick % len(colors)]
503
+
504
+ result.append(f"{frame} ", style=f"bold {color}")
505
+
506
+ # Thinking text
507
+ display_text = self._text or "Processing..."
508
+ if len(display_text) > 60:
509
+ display_text = display_text[:57] + "..."
510
+
511
+ result.append(display_text, style=f"italic {COLORS.text_muted}")
512
+
513
+ return result
514
+
515
+
516
+ # ============================================================================
517
+ # DISPLAY HELPER FUNCTIONS
518
+ # ============================================================================
519
+
520
+
521
+ def show_agent_header(
522
+ name: str,
523
+ model: str,
524
+ approval_mode: str = "ask",
525
+ ) -> Text:
526
+ """
527
+ Create a SuperQode-style agent header.
528
+
529
+ Clean, minimal, informative.
530
+ """
531
+ text = Text()
532
+ text.append("\n")
533
+
534
+ # Gradient line
535
+ line = "─" * 60
536
+ for i, char in enumerate(line):
537
+ color = GRADIENT_PURPLE[i % len(GRADIENT_PURPLE)]
538
+ text.append(char, style=color)
539
+ text.append("\n")
540
+
541
+ # Agent name
542
+ text.append(" ◈ ", style=f"bold {COLORS.primary}")
543
+ text.append(name.upper(), style=f"bold {COLORS.text_primary}")
544
+ text.append(" is working\n", style=COLORS.text_muted)
545
+
546
+ # Model
547
+ text.append(" Model: ", style=COLORS.text_dim)
548
+ text.append(model, style=f"bold {COLORS.info}")
549
+
550
+ # Approval mode
551
+ mode_styles = {
552
+ "auto": (COLORS.success, "AUTO"),
553
+ "ask": (COLORS.warning, "ASK"),
554
+ "deny": (COLORS.error, "DENY"),
555
+ }
556
+ color, label = mode_styles.get(approval_mode, (COLORS.warning, "ASK"))
557
+ text.append(" │ ", style=COLORS.text_ghost)
558
+ text.append(f"● {label}", style=f"bold {color}")
559
+
560
+ text.append("\n")
561
+
562
+ return text
563
+
564
+
565
+ def show_tool_call(
566
+ name: str,
567
+ kind: str,
568
+ arguments: Dict[str, Any] = None,
569
+ status: str = "running",
570
+ ) -> Text:
571
+ """
572
+ Create a SuperQode-style tool call display.
573
+
574
+ Minimal, left-border indicator, clean.
575
+ """
576
+ text = Text()
577
+
578
+ # Status icon
579
+ status_map = {
580
+ "pending": ("○", COLORS.text_dim),
581
+ "running": ("◐", COLORS.primary_light),
582
+ "success": ("✦", COLORS.success),
583
+ "error": ("✕", COLORS.error),
584
+ }
585
+ icon, color = status_map.get(status, ("•", COLORS.text_dim))
586
+
587
+ # Kind icon
588
+ kind_icons = {
589
+ "read": "↳",
590
+ "write": "↲",
591
+ "edit": "⟳",
592
+ "shell": "▸",
593
+ "search": "⌕",
594
+ "glob": "⋮",
595
+ }
596
+ kind_icon = kind_icons.get(kind.lower(), "•")
597
+
598
+ text.append(f" {icon} ", style=f"bold {color}")
599
+ text.append(f"{kind_icon} ", style=COLORS.text_dim)
600
+ text.append(name, style=COLORS.text_secondary)
601
+
602
+ # File path if present
603
+ if arguments:
604
+ path = arguments.get("path", arguments.get("file_path", arguments.get("filePath", "")))
605
+ if path:
606
+ text.append(f" {path}", style=COLORS.text_ghost)
607
+
608
+ # Command for shell
609
+ cmd = arguments.get("command", "")
610
+ if cmd:
611
+ cmd_short = cmd[:40] + "..." if len(cmd) > 40 else cmd
612
+ text.append(f" $ {cmd_short}", style=COLORS.text_ghost)
613
+
614
+ text.append("\n")
615
+ return text
616
+
617
+
618
+ def show_thinking(text: str, tick: int = 0) -> Text:
619
+ """
620
+ Create a SuperQode-style thinking line.
621
+
622
+ Animated quantum dots, minimal.
623
+ """
624
+ result = Text()
625
+
626
+ # Animated prefix
627
+ frames = ["◇", "◆", "◈", "◆"]
628
+ frame = frames[tick % len(frames)]
629
+ color = GRADIENT_PURPLE[tick % len(GRADIENT_PURPLE)]
630
+
631
+ result.append(f" {frame} ", style=f"bold {color}")
632
+
633
+ # Text
634
+ display = text[:70] + "..." if len(text) > 70 else text
635
+ result.append(display, style=f"italic {COLORS.text_muted}")
636
+ result.append("\n")
637
+
638
+ return result
639
+
640
+
641
+ def show_response(
642
+ content: str,
643
+ agent_name: str = "",
644
+ duration: float = 0,
645
+ token_count: int = 0,
646
+ tool_count: int = 0,
647
+ files_modified: List[str] = None,
648
+ ) -> Group:
649
+ """
650
+ Create a SuperQode-style response display.
651
+
652
+ Clean header, markdown content, stats footer.
653
+ """
654
+ items = []
655
+
656
+ # Success header
657
+ header = Text()
658
+ header.append("\n")
659
+
660
+ # Gradient line
661
+ line = "─" * 60
662
+ for i, char in enumerate(line):
663
+ color = ["#10b981", "#14b8a6", "#06b6d4", "#0ea5e9"][i % 4]
664
+ header.append(char, style=color)
665
+ header.append("\n")
666
+
667
+ # Agent name with checkmark
668
+ header.append(" ✦ ", style=f"bold {COLORS.success}")
669
+ if agent_name:
670
+ header.append(agent_name, style=f"bold {COLORS.text_primary}")
671
+ header.append(" completed", style=COLORS.text_muted)
672
+ else:
673
+ header.append("Task completed", style=f"bold {COLORS.text_primary}")
674
+ header.append("\n")
675
+
676
+ items.append(header)
677
+
678
+ # Stats line
679
+ stats = Text()
680
+ stats.append(" ")
681
+
682
+ if duration > 0:
683
+ stats.append(f"⏱ {duration:.1f}s", style=COLORS.text_dim)
684
+
685
+ if tool_count > 0:
686
+ stats.append(f" │ 🔧 {tool_count} tools", style=COLORS.text_dim)
687
+
688
+ if files_modified:
689
+ stats.append(f" │ 📝 {len(files_modified)} files", style=COLORS.text_dim)
690
+
691
+ if token_count > 0:
692
+ stats.append(f" │ 📊 {token_count} tokens", style=COLORS.text_dim)
693
+
694
+ stats.append("\n\n")
695
+ items.append(stats)
696
+
697
+ # Content
698
+ if content.strip():
699
+ # Try to render as markdown
700
+ try:
701
+ md = Markdown(content)
702
+ items.append(md)
703
+ except Exception:
704
+ items.append(Text(content, style=COLORS.text_secondary))
705
+
706
+ # Footer line
707
+ footer = Text()
708
+ footer.append("\n")
709
+ for i, char in enumerate("─" * 60):
710
+ footer.append(char, style=COLORS.text_ghost)
711
+ footer.append("\n")
712
+ items.append(footer)
713
+
714
+ return Group(*items)
715
+
716
+
717
+ def show_completion_summary(
718
+ agent_name: str,
719
+ duration: float,
720
+ tool_count: int,
721
+ files_modified: List[str] = None,
722
+ files_read: List[str] = None,
723
+ ) -> Text:
724
+ """
725
+ Show a brief completion summary when there's no text response.
726
+ """
727
+ text = Text()
728
+ text.append("\n")
729
+
730
+ # Success line
731
+ for i, char in enumerate("─" * 40):
732
+ color = ["#10b981", "#14b8a6", "#06b6d4"][i % 3]
733
+ text.append(char, style=color)
734
+ text.append("\n")
735
+
736
+ text.append(" ✦ ", style=f"bold {COLORS.success}")
737
+ text.append(f"{agent_name} ", style=f"bold {COLORS.text_primary}")
738
+ text.append("finished", style=COLORS.text_muted)
739
+
740
+ # Stats
741
+ stats = []
742
+ if duration > 0:
743
+ stats.append(f"{duration:.1f}s")
744
+ if tool_count > 0:
745
+ stats.append(f"{tool_count} tools")
746
+ if files_modified:
747
+ stats.append(f"{len(files_modified)} files modified")
748
+
749
+ if stats:
750
+ text.append(f" ({', '.join(stats)})", style=COLORS.text_dim)
751
+
752
+ text.append("\n")
753
+
754
+ # File list if any
755
+ if files_modified:
756
+ text.append("\n 📝 Modified:\n", style=COLORS.text_muted)
757
+ for f in files_modified[:5]:
758
+ text.append(f" {f}\n", style=COLORS.text_dim)
759
+ if len(files_modified) > 5:
760
+ text.append(f" ... and {len(files_modified) - 5} more\n", style=COLORS.text_ghost)
761
+
762
+ return text
763
+
764
+
765
+ # ============================================================================
766
+ # EXPORTS
767
+ # ============================================================================
768
+
769
+ __all__ = [
770
+ # Enums
771
+ "ToolStatus",
772
+ "AgentState",
773
+ # Data classes
774
+ "ToolCallInfo",
775
+ "SessionStats",
776
+ # Widgets
777
+ "EnhancedStatusHeader",
778
+ "EnhancedToolPanel",
779
+ "EnhancedThinkingBar",
780
+ # Functions
781
+ "show_agent_header",
782
+ "show_tool_call",
783
+ "show_thinking",
784
+ "show_response",
785
+ "show_completion_summary",
786
+ ]