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
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode TUI Widgets - Reusable UI components.
|
|
3
|
+
|
|
4
|
+
Provides enhanced widgets for the TUI:
|
|
5
|
+
- ToolDisplay: Shows tool calls with status
|
|
6
|
+
- ProgressPanel: Session progress tracking
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from superqode.tui_widgets import ToolDisplay, ProgressPanel
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# Re-export widgets
|
|
13
|
+
from .widgets import (
|
|
14
|
+
ToolDisplay,
|
|
15
|
+
ToolCall,
|
|
16
|
+
ToolStatus,
|
|
17
|
+
ProgressPanel,
|
|
18
|
+
ProgressStep,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"ToolDisplay",
|
|
23
|
+
"ToolCall",
|
|
24
|
+
"ToolStatus",
|
|
25
|
+
"ProgressPanel",
|
|
26
|
+
"ProgressStep",
|
|
27
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode TUI Widgets - Reusable UI components.
|
|
3
|
+
|
|
4
|
+
Provides enhanced widgets for the TUI:
|
|
5
|
+
- ToolDisplay: Shows tool calls with status
|
|
6
|
+
- ProgressPanel: Session progress tracking
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .tool_display import ToolDisplay, ToolCall, ToolStatus
|
|
10
|
+
from .progress import ProgressPanel, ProgressStep
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ToolDisplay",
|
|
14
|
+
"ToolCall",
|
|
15
|
+
"ToolStatus",
|
|
16
|
+
"ProgressPanel",
|
|
17
|
+
"ProgressStep",
|
|
18
|
+
]
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Progress Panel Widget - Shows session progress.
|
|
3
|
+
|
|
4
|
+
Displays the current session progress with steps:
|
|
5
|
+
- Completed steps
|
|
6
|
+
- Current step (with spinner)
|
|
7
|
+
- Pending steps
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from typing import List, Optional
|
|
13
|
+
|
|
14
|
+
from rich.console import Console, Group
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
from rich.text import Text
|
|
17
|
+
from rich.box import ROUNDED
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StepStatus(Enum):
|
|
21
|
+
"""Status of a progress step."""
|
|
22
|
+
|
|
23
|
+
PENDING = "pending"
|
|
24
|
+
IN_PROGRESS = "in_progress"
|
|
25
|
+
COMPLETE = "complete"
|
|
26
|
+
SKIPPED = "skipped"
|
|
27
|
+
ERROR = "error"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ProgressStep:
|
|
32
|
+
"""Represents a single progress step."""
|
|
33
|
+
|
|
34
|
+
name: str
|
|
35
|
+
description: str = ""
|
|
36
|
+
status: StepStatus = StepStatus.PENDING
|
|
37
|
+
substeps: List[str] = field(default_factory=list)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def status_icon(self) -> str:
|
|
41
|
+
"""Get status icon."""
|
|
42
|
+
return {
|
|
43
|
+
StepStatus.PENDING: "[dim]○[/dim]",
|
|
44
|
+
StepStatus.IN_PROGRESS: "[yellow]⟳[/yellow]",
|
|
45
|
+
StepStatus.COMPLETE: "[green]✓[/green]",
|
|
46
|
+
StepStatus.SKIPPED: "[dim]⊘[/dim]",
|
|
47
|
+
StepStatus.ERROR: "[red]✗[/red]",
|
|
48
|
+
}.get(self.status, "?")
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def style(self) -> str:
|
|
52
|
+
"""Get text style based on status."""
|
|
53
|
+
return {
|
|
54
|
+
StepStatus.PENDING: "dim",
|
|
55
|
+
StepStatus.IN_PROGRESS: "bold yellow",
|
|
56
|
+
StepStatus.COMPLETE: "green",
|
|
57
|
+
StepStatus.SKIPPED: "dim",
|
|
58
|
+
StepStatus.ERROR: "red",
|
|
59
|
+
}.get(self.status, "")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ProgressPanel:
|
|
63
|
+
"""
|
|
64
|
+
Widget for displaying session progress.
|
|
65
|
+
|
|
66
|
+
Shows a panel with progress steps and their status.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
title: str = "Session Progress",
|
|
72
|
+
role: Optional[str] = None,
|
|
73
|
+
mode: Optional[str] = None,
|
|
74
|
+
):
|
|
75
|
+
"""
|
|
76
|
+
Initialize the progress panel.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
title: Panel title
|
|
80
|
+
role: Current role (e.g., "qe.security")
|
|
81
|
+
mode: Current mode (e.g., "Deep Scan")
|
|
82
|
+
"""
|
|
83
|
+
self.title = title
|
|
84
|
+
self.role = role
|
|
85
|
+
self.mode = mode
|
|
86
|
+
self.steps: List[ProgressStep] = []
|
|
87
|
+
self.console = Console()
|
|
88
|
+
|
|
89
|
+
def add_step(self, name: str, description: str = "") -> ProgressStep:
|
|
90
|
+
"""Add a new step."""
|
|
91
|
+
step = ProgressStep(name=name, description=description)
|
|
92
|
+
self.steps.append(step)
|
|
93
|
+
return step
|
|
94
|
+
|
|
95
|
+
def start_step(self, step: ProgressStep) -> None:
|
|
96
|
+
"""Mark a step as in progress."""
|
|
97
|
+
step.status = StepStatus.IN_PROGRESS
|
|
98
|
+
|
|
99
|
+
def complete_step(self, step: ProgressStep) -> None:
|
|
100
|
+
"""Mark a step as complete."""
|
|
101
|
+
step.status = StepStatus.COMPLETE
|
|
102
|
+
|
|
103
|
+
def error_step(self, step: ProgressStep) -> None:
|
|
104
|
+
"""Mark a step as errored."""
|
|
105
|
+
step.status = StepStatus.ERROR
|
|
106
|
+
|
|
107
|
+
def skip_step(self, step: ProgressStep) -> None:
|
|
108
|
+
"""Mark a step as skipped."""
|
|
109
|
+
step.status = StepStatus.SKIPPED
|
|
110
|
+
|
|
111
|
+
def get_current_step(self) -> Optional[ProgressStep]:
|
|
112
|
+
"""Get the current in-progress step."""
|
|
113
|
+
for step in self.steps:
|
|
114
|
+
if step.status == StepStatus.IN_PROGRESS:
|
|
115
|
+
return step
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
def render(self) -> Panel:
|
|
119
|
+
"""Render the progress panel as a Rich Panel."""
|
|
120
|
+
lines = []
|
|
121
|
+
|
|
122
|
+
# Header with role and mode
|
|
123
|
+
if self.role or self.mode:
|
|
124
|
+
header_parts = []
|
|
125
|
+
if self.role:
|
|
126
|
+
header_parts.append(f"[cyan]Role:[/cyan] {self.role}")
|
|
127
|
+
if self.mode:
|
|
128
|
+
header_parts.append(f"[cyan]Mode:[/cyan] {self.mode}")
|
|
129
|
+
lines.append(Text.from_markup(" ".join(header_parts)))
|
|
130
|
+
lines.append(Text()) # Empty line
|
|
131
|
+
|
|
132
|
+
# Progress steps
|
|
133
|
+
if not self.steps:
|
|
134
|
+
lines.append(Text("No steps defined", style="dim"))
|
|
135
|
+
else:
|
|
136
|
+
for step in self.steps:
|
|
137
|
+
# Main step line
|
|
138
|
+
step_text = Text()
|
|
139
|
+
step_text.append(step.status_icon)
|
|
140
|
+
step_text.append(" ")
|
|
141
|
+
step_text.append(step.name, style=step.style)
|
|
142
|
+
if step.description and step.status == StepStatus.IN_PROGRESS:
|
|
143
|
+
step_text.append(f" - {step.description}", style="dim")
|
|
144
|
+
lines.append(step_text)
|
|
145
|
+
|
|
146
|
+
# Substeps (indented)
|
|
147
|
+
if step.substeps and step.status == StepStatus.IN_PROGRESS:
|
|
148
|
+
for substep in step.substeps[-3:]: # Show last 3 substeps
|
|
149
|
+
lines.append(Text(f" {substep}", style="dim"))
|
|
150
|
+
|
|
151
|
+
content = Group(*lines)
|
|
152
|
+
|
|
153
|
+
return Panel(
|
|
154
|
+
content,
|
|
155
|
+
title=f"[bold]{self.title}[/bold]",
|
|
156
|
+
border_style="cyan",
|
|
157
|
+
box=ROUNDED,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def print(self) -> None:
|
|
161
|
+
"""Print the progress panel to console."""
|
|
162
|
+
self.console.print(self.render())
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def create_qe_progress(role: str, mode: str) -> ProgressPanel:
|
|
166
|
+
"""
|
|
167
|
+
Create a progress panel for a QE session.
|
|
168
|
+
|
|
169
|
+
Pre-populates with standard QE steps.
|
|
170
|
+
"""
|
|
171
|
+
panel = ProgressPanel(
|
|
172
|
+
title="SuperQE Session",
|
|
173
|
+
role=role,
|
|
174
|
+
mode=mode,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
panel.add_step("Initializing workspace", "Setting up ephemeral environment")
|
|
178
|
+
panel.add_step("Running test discovery", "Finding test files and suites")
|
|
179
|
+
panel.add_step("Executing tests", "Running smoke/sanity/regression")
|
|
180
|
+
panel.add_step("Analyzing results", "Processing test outcomes")
|
|
181
|
+
panel.add_step("Detecting issues", "Proactive vulnerability scanning")
|
|
182
|
+
panel.add_step("Generating QR", "Creating quality report")
|
|
183
|
+
panel.add_step("Cleaning up", "Reverting changes, preserving artifacts")
|
|
184
|
+
|
|
185
|
+
return panel
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool Display Widget - Shows tool calls with status.
|
|
3
|
+
|
|
4
|
+
Displays tool invocations in a clear, structured format:
|
|
5
|
+
- Tool name and type
|
|
6
|
+
- Arguments (truncated for readability)
|
|
7
|
+
- Status (pending, running, complete, error)
|
|
8
|
+
- Duration and result summary
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Any, Dict, List, Optional
|
|
15
|
+
|
|
16
|
+
from rich.console import Console, Group
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
from rich.table import Table
|
|
19
|
+
from rich.text import Text
|
|
20
|
+
from rich.box import ROUNDED
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ToolStatus(Enum):
|
|
24
|
+
"""Status of a tool call."""
|
|
25
|
+
|
|
26
|
+
PENDING = "pending"
|
|
27
|
+
RUNNING = "running"
|
|
28
|
+
COMPLETE = "complete"
|
|
29
|
+
ERROR = "error"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ToolCall:
|
|
34
|
+
"""Represents a single tool invocation."""
|
|
35
|
+
|
|
36
|
+
name: str
|
|
37
|
+
tool_type: str # e.g., "file_read", "shell_exec", "edit"
|
|
38
|
+
arguments: Dict[str, Any] = field(default_factory=dict)
|
|
39
|
+
status: ToolStatus = ToolStatus.PENDING
|
|
40
|
+
start_time: Optional[datetime] = None
|
|
41
|
+
end_time: Optional[datetime] = None
|
|
42
|
+
result_summary: str = ""
|
|
43
|
+
error_message: str = ""
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def duration_ms(self) -> Optional[float]:
|
|
47
|
+
"""Get duration in milliseconds."""
|
|
48
|
+
if self.start_time and self.end_time:
|
|
49
|
+
return (self.end_time - self.start_time).total_seconds() * 1000
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def duration_str(self) -> str:
|
|
54
|
+
"""Get human-readable duration."""
|
|
55
|
+
if self.duration_ms is None:
|
|
56
|
+
return "-"
|
|
57
|
+
if self.duration_ms < 1000:
|
|
58
|
+
return f"{self.duration_ms:.0f}ms"
|
|
59
|
+
return f"{self.duration_ms / 1000:.1f}s"
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def status_icon(self) -> str:
|
|
63
|
+
"""Get status icon."""
|
|
64
|
+
return {
|
|
65
|
+
ToolStatus.PENDING: "[dim]○[/dim]",
|
|
66
|
+
ToolStatus.RUNNING: "[yellow]⟳[/yellow]",
|
|
67
|
+
ToolStatus.COMPLETE: "[green]✓[/green]",
|
|
68
|
+
ToolStatus.ERROR: "[red]✗[/red]",
|
|
69
|
+
}.get(self.status, "?")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ToolDisplay:
|
|
73
|
+
"""
|
|
74
|
+
Widget for displaying tool calls in the TUI.
|
|
75
|
+
|
|
76
|
+
Shows a panel with recent tool invocations and their status.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __init__(self, max_display: int = 5, show_arguments: bool = False):
|
|
80
|
+
"""
|
|
81
|
+
Initialize the tool display.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
max_display: Maximum number of tools to show
|
|
85
|
+
show_arguments: Whether to show tool arguments
|
|
86
|
+
"""
|
|
87
|
+
self.max_display = max_display
|
|
88
|
+
self.show_arguments = show_arguments
|
|
89
|
+
self.calls: List[ToolCall] = []
|
|
90
|
+
self.console = Console()
|
|
91
|
+
|
|
92
|
+
def add_call(self, call: ToolCall) -> None:
|
|
93
|
+
"""Add a new tool call."""
|
|
94
|
+
self.calls.append(call)
|
|
95
|
+
# Keep only the most recent calls
|
|
96
|
+
if len(self.calls) > self.max_display * 2:
|
|
97
|
+
self.calls = self.calls[-self.max_display :]
|
|
98
|
+
|
|
99
|
+
def start_call(self, name: str, tool_type: str, arguments: Dict[str, Any] = None) -> ToolCall:
|
|
100
|
+
"""Create and start a new tool call."""
|
|
101
|
+
call = ToolCall(
|
|
102
|
+
name=name,
|
|
103
|
+
tool_type=tool_type,
|
|
104
|
+
arguments=arguments or {},
|
|
105
|
+
status=ToolStatus.RUNNING,
|
|
106
|
+
start_time=datetime.now(),
|
|
107
|
+
)
|
|
108
|
+
self.add_call(call)
|
|
109
|
+
return call
|
|
110
|
+
|
|
111
|
+
def complete_call(self, call: ToolCall, result_summary: str = "") -> None:
|
|
112
|
+
"""Mark a tool call as complete."""
|
|
113
|
+
call.status = ToolStatus.COMPLETE
|
|
114
|
+
call.end_time = datetime.now()
|
|
115
|
+
call.result_summary = result_summary
|
|
116
|
+
|
|
117
|
+
def error_call(self, call: ToolCall, error_message: str) -> None:
|
|
118
|
+
"""Mark a tool call as errored."""
|
|
119
|
+
call.status = ToolStatus.ERROR
|
|
120
|
+
call.end_time = datetime.now()
|
|
121
|
+
call.error_message = error_message
|
|
122
|
+
|
|
123
|
+
def _truncate(self, text: str, max_len: int = 50) -> str:
|
|
124
|
+
"""Truncate text to max length."""
|
|
125
|
+
if len(text) <= max_len:
|
|
126
|
+
return text
|
|
127
|
+
return text[: max_len - 3] + "..."
|
|
128
|
+
|
|
129
|
+
def _format_arguments(self, args: Dict[str, Any]) -> str:
|
|
130
|
+
"""Format arguments for display."""
|
|
131
|
+
if not args:
|
|
132
|
+
return ""
|
|
133
|
+
parts = []
|
|
134
|
+
for key, value in args.items():
|
|
135
|
+
if isinstance(value, str):
|
|
136
|
+
parts.append(f"{key}={self._truncate(repr(value), 30)}")
|
|
137
|
+
else:
|
|
138
|
+
parts.append(f"{key}={value}")
|
|
139
|
+
return ", ".join(parts[:3]) # Show at most 3 args
|
|
140
|
+
|
|
141
|
+
def render(self) -> Panel:
|
|
142
|
+
"""Render the tool display as a Rich Panel."""
|
|
143
|
+
recent = self.calls[-self.max_display :]
|
|
144
|
+
|
|
145
|
+
if not recent:
|
|
146
|
+
content = Text("No tool calls yet", style="dim")
|
|
147
|
+
else:
|
|
148
|
+
table = Table(show_header=False, box=None, padding=(0, 1))
|
|
149
|
+
table.add_column("Status", width=3)
|
|
150
|
+
table.add_column("Tool", style="cyan")
|
|
151
|
+
table.add_column("Info", style="dim")
|
|
152
|
+
table.add_column("Time", width=8, justify="right")
|
|
153
|
+
|
|
154
|
+
for call in recent:
|
|
155
|
+
# Build info column
|
|
156
|
+
if call.status == ToolStatus.ERROR:
|
|
157
|
+
info = Text(self._truncate(call.error_message, 40), style="red")
|
|
158
|
+
elif call.result_summary:
|
|
159
|
+
info = Text(self._truncate(call.result_summary, 40))
|
|
160
|
+
elif self.show_arguments:
|
|
161
|
+
info = Text(self._truncate(self._format_arguments(call.arguments), 40))
|
|
162
|
+
else:
|
|
163
|
+
info = Text(call.tool_type, style="dim")
|
|
164
|
+
|
|
165
|
+
table.add_row(
|
|
166
|
+
call.status_icon,
|
|
167
|
+
call.name,
|
|
168
|
+
info,
|
|
169
|
+
call.duration_str,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
content = table
|
|
173
|
+
|
|
174
|
+
return Panel(
|
|
175
|
+
content,
|
|
176
|
+
title="[bold]Tool Calls[/bold]",
|
|
177
|
+
border_style="dim",
|
|
178
|
+
box=ROUNDED,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def print(self) -> None:
|
|
182
|
+
"""Print the tool display to console."""
|
|
183
|
+
self.console.print(self.render())
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def create_tool_display(max_display: int = 5) -> ToolDisplay:
|
|
187
|
+
"""Factory function to create a ToolDisplay."""
|
|
188
|
+
return ToolDisplay(max_display=max_display)
|