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
superqode/plan.py ADDED
@@ -0,0 +1,323 @@
1
+ """
2
+ SuperQode Plan Widget - Task Planning & Progress Display
3
+
4
+ A beautiful task plan visualization with:
5
+ - Priority levels with color coding
6
+ - Status tracking (pending, in_progress, completed, failed)
7
+ - Progress animations
8
+ - Gradient styling
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass, field
14
+ from enum import Enum
15
+ from typing import List, Optional, Callable
16
+ from datetime import datetime
17
+
18
+ from rich.console import Console
19
+ from rich.panel import Panel
20
+ from rich.text import Text
21
+ from rich.table import Table
22
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
23
+ from rich.box import ROUNDED, SIMPLE
24
+
25
+
26
+ class TaskStatus(Enum):
27
+ """Task status values."""
28
+
29
+ PENDING = "pending"
30
+ IN_PROGRESS = "in_progress"
31
+ COMPLETED = "completed"
32
+ FAILED = "failed"
33
+ SKIPPED = "skipped"
34
+
35
+
36
+ class TaskPriority(Enum):
37
+ """Task priority levels."""
38
+
39
+ LOW = "low"
40
+ MEDIUM = "medium"
41
+ HIGH = "high"
42
+ CRITICAL = "critical"
43
+
44
+
45
+ @dataclass
46
+ class PlanTask:
47
+ """A single task in the plan."""
48
+
49
+ id: str
50
+ content: str
51
+ status: TaskStatus = TaskStatus.PENDING
52
+ priority: TaskPriority = TaskPriority.MEDIUM
53
+ created_at: datetime = field(default_factory=datetime.now)
54
+ completed_at: Optional[datetime] = None
55
+ parent_id: Optional[str] = None
56
+ subtasks: List[str] = field(default_factory=list)
57
+ metadata: dict = field(default_factory=dict)
58
+
59
+
60
+ # SuperQode plan colors
61
+ PLAN_COLORS = {
62
+ # Status colors
63
+ "pending": "#71717a",
64
+ "in_progress": "#06b6d4",
65
+ "completed": "#22c55e",
66
+ "failed": "#ef4444",
67
+ "skipped": "#52525b",
68
+ # Priority colors
69
+ "low": "#3b82f6",
70
+ "medium": "#f59e0b",
71
+ "high": "#f97316",
72
+ "critical": "#ef4444",
73
+ # UI colors
74
+ "header": "#a855f7",
75
+ "border": "#2a2a2a",
76
+ "progress_bar": "#ec4899",
77
+ }
78
+
79
+ # Status icons
80
+ STATUS_ICONS = {
81
+ TaskStatus.PENDING: "⏳",
82
+ TaskStatus.IN_PROGRESS: "🔄",
83
+ TaskStatus.COMPLETED: "✅",
84
+ TaskStatus.FAILED: "❌",
85
+ TaskStatus.SKIPPED: "⏭️",
86
+ }
87
+
88
+ # Priority icons
89
+ PRIORITY_ICONS = {
90
+ TaskPriority.LOW: "🔵",
91
+ TaskPriority.MEDIUM: "🟡",
92
+ TaskPriority.HIGH: "🟠",
93
+ TaskPriority.CRITICAL: "🔴",
94
+ }
95
+
96
+
97
+ class PlanManager:
98
+ """Manages task plans and progress tracking."""
99
+
100
+ def __init__(self):
101
+ self.tasks: List[PlanTask] = []
102
+ self.current_plan_name: str = "Agent Plan"
103
+ self._task_counter = 0
104
+
105
+ def _generate_id(self) -> str:
106
+ """Generate a unique task ID."""
107
+ self._task_counter += 1
108
+ return f"task_{self._task_counter}"
109
+
110
+ def add_task(
111
+ self,
112
+ content: str,
113
+ priority: TaskPriority = TaskPriority.MEDIUM,
114
+ parent_id: Optional[str] = None,
115
+ ) -> PlanTask:
116
+ """Add a new task to the plan."""
117
+ task = PlanTask(
118
+ id=self._generate_id(), content=content, priority=priority, parent_id=parent_id
119
+ )
120
+ self.tasks.append(task)
121
+
122
+ # Link to parent if specified
123
+ if parent_id:
124
+ for t in self.tasks:
125
+ if t.id == parent_id:
126
+ t.subtasks.append(task.id)
127
+ break
128
+
129
+ return task
130
+
131
+ def update_status(self, task_id: str, status: TaskStatus) -> bool:
132
+ """Update a task's status."""
133
+ for task in self.tasks:
134
+ if task.id == task_id:
135
+ task.status = status
136
+ if status == TaskStatus.COMPLETED:
137
+ task.completed_at = datetime.now()
138
+ return True
139
+ return False
140
+
141
+ def start_task(self, task_id: str) -> bool:
142
+ """Mark a task as in progress."""
143
+ return self.update_status(task_id, TaskStatus.IN_PROGRESS)
144
+
145
+ def complete_task(self, task_id: str) -> bool:
146
+ """Mark a task as completed."""
147
+ return self.update_status(task_id, TaskStatus.COMPLETED)
148
+
149
+ def fail_task(self, task_id: str) -> bool:
150
+ """Mark a task as failed."""
151
+ return self.update_status(task_id, TaskStatus.FAILED)
152
+
153
+ def get_progress(self) -> tuple:
154
+ """Get progress statistics."""
155
+ total = len(self.tasks)
156
+ if total == 0:
157
+ return 0, 0, 0.0
158
+
159
+ completed = sum(1 for t in self.tasks if t.status == TaskStatus.COMPLETED)
160
+ in_progress = sum(1 for t in self.tasks if t.status == TaskStatus.IN_PROGRESS)
161
+ percentage = (completed / total) * 100
162
+
163
+ return completed, total, percentage
164
+
165
+ def get_current_task(self) -> Optional[PlanTask]:
166
+ """Get the currently active task."""
167
+ for task in self.tasks:
168
+ if task.status == TaskStatus.IN_PROGRESS:
169
+ return task
170
+ return None
171
+
172
+ def get_next_task(self) -> Optional[PlanTask]:
173
+ """Get the next pending task."""
174
+ # Sort by priority (critical first)
175
+ priority_order = {
176
+ TaskPriority.CRITICAL: 0,
177
+ TaskPriority.HIGH: 1,
178
+ TaskPriority.MEDIUM: 2,
179
+ TaskPriority.LOW: 3,
180
+ }
181
+
182
+ pending = [t for t in self.tasks if t.status == TaskStatus.PENDING]
183
+ if not pending:
184
+ return None
185
+
186
+ pending.sort(key=lambda t: priority_order.get(t.priority, 2))
187
+ return pending[0]
188
+
189
+ def clear(self) -> None:
190
+ """Clear all tasks."""
191
+ self.tasks.clear()
192
+ self._task_counter = 0
193
+
194
+ def from_list(self, items: List[str], priority: TaskPriority = TaskPriority.MEDIUM) -> None:
195
+ """Create tasks from a list of strings."""
196
+ self.clear()
197
+ for item in items:
198
+ self.add_task(item, priority)
199
+
200
+
201
+ def render_plan(manager: PlanManager, console: Console, show_completed: bool = True) -> None:
202
+ """Render the task plan with beautiful styling."""
203
+ if not manager.tasks:
204
+ console.print(" [dim]No plan yet[/dim]")
205
+ return
206
+
207
+ # Progress header
208
+ completed, total, percentage = manager.get_progress()
209
+
210
+ header = Text()
211
+ header.append(f" 📋 ", style="bold")
212
+ header.append(manager.current_plan_name, style="bold white")
213
+ header.append(" ", style="")
214
+ header.append(f"{completed}/{total}", style=f"bold {PLAN_COLORS['completed']}")
215
+ header.append(f" ({percentage:.0f}%)", style="dim")
216
+
217
+ console.print(Panel(header, border_style=PLAN_COLORS["header"], box=ROUNDED, padding=(0, 1)))
218
+
219
+ # Progress bar
220
+ bar_width = 40
221
+ filled = int((percentage / 100) * bar_width)
222
+ empty = bar_width - filled
223
+
224
+ bar = Text()
225
+ bar.append(" ", style="")
226
+ bar.append("█" * filled, style=PLAN_COLORS["progress_bar"])
227
+ bar.append("░" * empty, style="dim")
228
+ bar.append(f" {percentage:.0f}%", style="bold white")
229
+ console.print(bar)
230
+ console.print()
231
+
232
+ # Task list
233
+ for i, task in enumerate(manager.tasks):
234
+ if not show_completed and task.status == TaskStatus.COMPLETED:
235
+ continue
236
+
237
+ render_task(task, console, index=i + 1)
238
+
239
+
240
+ def render_task(task: PlanTask, console: Console, index: int = 0, indent: int = 0) -> None:
241
+ """Render a single task."""
242
+ status_icon = STATUS_ICONS.get(task.status, "○")
243
+ priority_icon = PRIORITY_ICONS.get(task.priority, "")
244
+ status_color = PLAN_COLORS.get(task.status.value, PLAN_COLORS["pending"])
245
+
246
+ # Build task line
247
+ line = Text()
248
+ line.append(" " * indent, style="")
249
+
250
+ # Index
251
+ if index > 0:
252
+ line.append(f"{index:>2}. ", style="dim")
253
+
254
+ # Status icon
255
+ line.append(f"{status_icon} ", style=status_color)
256
+
257
+ # Priority indicator (only for non-completed)
258
+ if task.status != TaskStatus.COMPLETED and task.priority in (
259
+ TaskPriority.HIGH,
260
+ TaskPriority.CRITICAL,
261
+ ):
262
+ line.append(f"{priority_icon} ", style="")
263
+
264
+ # Content with strikethrough for completed
265
+ if task.status == TaskStatus.COMPLETED:
266
+ line.append(task.content, style=f"strike {status_color}")
267
+ elif task.status == TaskStatus.IN_PROGRESS:
268
+ line.append(task.content, style=f"bold {status_color}")
269
+ elif task.status == TaskStatus.FAILED:
270
+ line.append(task.content, style=f"{status_color}")
271
+ else:
272
+ line.append(task.content, style="white")
273
+
274
+ # Duration for completed tasks
275
+ if task.completed_at and task.created_at:
276
+ duration = (task.completed_at - task.created_at).total_seconds()
277
+ if duration < 60:
278
+ dur_str = f"{duration:.1f}s"
279
+ else:
280
+ dur_str = f"{duration / 60:.1f}m"
281
+ line.append(f" ({dur_str})", style="dim")
282
+
283
+ console.print(line)
284
+
285
+
286
+ def render_plan_compact(manager: PlanManager, console: Console) -> None:
287
+ """Render a compact one-line plan summary."""
288
+ if not manager.tasks:
289
+ return
290
+
291
+ completed, total, percentage = manager.get_progress()
292
+ current = manager.get_current_task()
293
+
294
+ line = Text()
295
+ line.append("📋 ", style="")
296
+ line.append(f"{completed}/{total}", style=f"bold {PLAN_COLORS['completed']}")
297
+
298
+ if current:
299
+ line.append(" │ ", style="dim")
300
+ line.append("🔄 ", style=PLAN_COLORS["in_progress"])
301
+ content = current.content[:40] + "..." if len(current.content) > 40 else current.content
302
+ line.append(content, style=PLAN_COLORS["in_progress"])
303
+
304
+ console.print(line)
305
+
306
+
307
+ def render_current_task(manager: PlanManager, console: Console) -> None:
308
+ """Render just the current task with emphasis."""
309
+ current = manager.get_current_task()
310
+ if not current:
311
+ next_task = manager.get_next_task()
312
+ if next_task:
313
+ console.print(f" [dim]Next:[/dim] {next_task.content}")
314
+ else:
315
+ console.print(" [green]✅ All tasks completed![/green]")
316
+ return
317
+
318
+ line = Text()
319
+ line.append(" 🔄 ", style=f"bold {PLAN_COLORS['in_progress']}")
320
+ line.append("Current: ", style="bold white")
321
+ line.append(current.content, style=f"bold {PLAN_COLORS['in_progress']}")
322
+
323
+ console.print(line)
@@ -0,0 +1,33 @@
1
+ """Provider and model management for SuperQode CLI."""
2
+
3
+ from superqode.providers.manager import ProviderManager, ProviderInfo, ModelInfo
4
+ from superqode.providers.registry import (
5
+ PROVIDERS,
6
+ ProviderDef,
7
+ ProviderTier,
8
+ ProviderCategory,
9
+ get_provider,
10
+ get_providers_by_category,
11
+ get_providers_by_tier,
12
+ get_all_provider_ids,
13
+ get_free_providers,
14
+ get_local_providers,
15
+ )
16
+
17
+ __all__ = [
18
+ # Legacy manager
19
+ "ProviderManager",
20
+ "ProviderInfo",
21
+ "ModelInfo",
22
+ # New registry
23
+ "PROVIDERS",
24
+ "ProviderDef",
25
+ "ProviderTier",
26
+ "ProviderCategory",
27
+ "get_provider",
28
+ "get_providers_by_category",
29
+ "get_providers_by_tier",
30
+ "get_all_provider_ids",
31
+ "get_free_providers",
32
+ "get_local_providers",
33
+ ]
@@ -0,0 +1,165 @@
1
+ """
2
+ Gateway module for BYOK mode.
3
+
4
+ Provides a pluggable gateway abstraction for LLM API calls.
5
+ Supports multiple gateway implementations:
6
+ - LiteLLM (default): Unified access to 100+ providers
7
+ - OpenResponses: Open Responses specification for local/custom providers
8
+
9
+ Usage:
10
+ # Create gateway using factory (recommended)
11
+ gateway = GatewayFactory.create("litellm")
12
+ gateway = GatewayFactory.create("openresponses", base_url="http://localhost:11434")
13
+
14
+ # Create gateway directly
15
+ gateway = LiteLLMGateway()
16
+ gateway = OpenResponsesGateway(base_url="http://localhost:11434")
17
+ """
18
+
19
+ from typing import Any, Dict, Optional
20
+
21
+ from .base import (
22
+ GatewayInterface,
23
+ GatewayError,
24
+ GatewayResponse,
25
+ AuthenticationError,
26
+ RateLimitError,
27
+ ModelNotFoundError,
28
+ InvalidRequestError,
29
+ Message,
30
+ ToolDefinition,
31
+ StreamChunk,
32
+ Usage,
33
+ Cost,
34
+ )
35
+ from .litellm_gateway import LiteLLMGateway
36
+
37
+
38
+ class GatewayFactory:
39
+ """
40
+ Factory for creating gateway instances.
41
+
42
+ Provides a unified interface for creating gateways of different types
43
+ based on configuration. Supports:
44
+ - litellm: LiteLLM-based gateway (default)
45
+ - openresponses: Open Responses specification gateway
46
+
47
+ Usage:
48
+ # Default LiteLLM gateway
49
+ gateway = GatewayFactory.create()
50
+
51
+ # Open Responses gateway with custom base URL
52
+ gateway = GatewayFactory.create(
53
+ "openresponses",
54
+ base_url="http://localhost:11434"
55
+ )
56
+
57
+ # With full configuration
58
+ gateway = GatewayFactory.create_from_config({
59
+ "type": "openresponses",
60
+ "base_url": "http://localhost:11434",
61
+ "reasoning_effort": "high",
62
+ })
63
+ """
64
+
65
+ # Default gateway type
66
+ DEFAULT_GATEWAY = "litellm"
67
+
68
+ # Registered gateway types
69
+ _registry: Dict[str, type] = {
70
+ "litellm": LiteLLMGateway,
71
+ }
72
+
73
+ @classmethod
74
+ def register(cls, name: str, gateway_class: type) -> None:
75
+ """
76
+ Register a gateway type.
77
+
78
+ Args:
79
+ name: Gateway type name
80
+ gateway_class: Gateway class implementing GatewayInterface
81
+ """
82
+ cls._registry[name] = gateway_class
83
+
84
+ @classmethod
85
+ def create(
86
+ cls,
87
+ gateway_type: Optional[str] = None,
88
+ **kwargs: Any,
89
+ ) -> GatewayInterface:
90
+ """
91
+ Create a gateway instance.
92
+
93
+ Args:
94
+ gateway_type: Type of gateway ("litellm", "openresponses")
95
+ **kwargs: Gateway-specific configuration
96
+
97
+ Returns:
98
+ Gateway instance
99
+
100
+ Raises:
101
+ ValueError: If gateway type is not registered
102
+ """
103
+ gateway_type = gateway_type or cls.DEFAULT_GATEWAY
104
+
105
+ # Lazy import OpenResponsesGateway to avoid circular imports
106
+ if gateway_type == "openresponses":
107
+ if "openresponses" not in cls._registry:
108
+ from .openresponses_gateway import OpenResponsesGateway
109
+
110
+ cls._registry["openresponses"] = OpenResponsesGateway
111
+
112
+ if gateway_type not in cls._registry:
113
+ available = ", ".join(cls._registry.keys())
114
+ raise ValueError(f"Unknown gateway type: {gateway_type}. Available: {available}")
115
+
116
+ gateway_class = cls._registry[gateway_type]
117
+ return gateway_class(**kwargs)
118
+
119
+ @classmethod
120
+ def create_from_config(
121
+ cls,
122
+ config: Dict[str, Any],
123
+ ) -> GatewayInterface:
124
+ """
125
+ Create a gateway from a configuration dict.
126
+
127
+ Args:
128
+ config: Configuration dict with "type" and gateway-specific options
129
+
130
+ Returns:
131
+ Gateway instance
132
+ """
133
+ config = config.copy()
134
+ gateway_type = config.pop("type", cls.DEFAULT_GATEWAY)
135
+ return cls.create(gateway_type, **config)
136
+
137
+ @classmethod
138
+ def get_available_types(cls) -> list[str]:
139
+ """Get list of available gateway types."""
140
+ # Include openresponses even if not yet imported
141
+ types = list(cls._registry.keys())
142
+ if "openresponses" not in types:
143
+ types.append("openresponses")
144
+ return types
145
+
146
+
147
+ __all__ = [
148
+ # Base classes
149
+ "GatewayInterface",
150
+ "GatewayError",
151
+ "GatewayResponse",
152
+ "AuthenticationError",
153
+ "RateLimitError",
154
+ "ModelNotFoundError",
155
+ "InvalidRequestError",
156
+ "Message",
157
+ "ToolDefinition",
158
+ "StreamChunk",
159
+ "Usage",
160
+ "Cost",
161
+ # Gateway implementations
162
+ "LiteLLMGateway",
163
+ # Factory
164
+ "GatewayFactory",
165
+ ]