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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- 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
|
+
]
|