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,380 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Roles CLI commands for SuperQode.
|
|
3
|
+
|
|
4
|
+
Commands for listing and showing role execution details.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
from rich.panel import Panel
|
|
14
|
+
from rich.text import Text
|
|
15
|
+
|
|
16
|
+
from ..config import load_config, load_enabled_modes, resolve_role
|
|
17
|
+
from ..execution.resolver import ExecutionResolver
|
|
18
|
+
from ..execution.modes import ExecutionMode, GatewayType
|
|
19
|
+
from ..providers.registry import PROVIDERS
|
|
20
|
+
from ..agents.registry import AGENTS, AgentStatus
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@click.group()
|
|
27
|
+
def roles():
|
|
28
|
+
"""Manage team roles and their execution configuration."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@roles.command("list")
|
|
33
|
+
@click.option("--mode", "-m", help="Filter by mode (e.g., dev, qe, devops)")
|
|
34
|
+
@click.option("--enabled-only", is_flag=True, help="Show only enabled roles")
|
|
35
|
+
def list_roles(mode: Optional[str], enabled_only: bool):
|
|
36
|
+
"""List all configured roles with their execution mode."""
|
|
37
|
+
|
|
38
|
+
config = load_config()
|
|
39
|
+
enabled_modes = load_enabled_modes(config)
|
|
40
|
+
|
|
41
|
+
if not enabled_modes:
|
|
42
|
+
console.print(
|
|
43
|
+
"[yellow]No roles configured. Run 'superqode init' to create default configuration.[/yellow]"
|
|
44
|
+
)
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
# Build table
|
|
48
|
+
table = Table(title="Team Roles", show_header=True, header_style="bold cyan")
|
|
49
|
+
table.add_column("Role", style="white")
|
|
50
|
+
table.add_column("Mode", style="dim")
|
|
51
|
+
table.add_column("Exec Mode", style="cyan")
|
|
52
|
+
table.add_column("Provider/Agent", style="green")
|
|
53
|
+
table.add_column("Model", style="dim")
|
|
54
|
+
table.add_column("Status", style="white")
|
|
55
|
+
|
|
56
|
+
for mode_name, mode_config in enabled_modes.items():
|
|
57
|
+
if mode and mode_name != mode:
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
if mode_config.roles:
|
|
61
|
+
for role_name, role_config in mode_config.roles.items():
|
|
62
|
+
if enabled_only and not role_config.enabled:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
# Determine execution mode display
|
|
66
|
+
exec_mode = role_config.execution_mode
|
|
67
|
+
if exec_mode == "acp":
|
|
68
|
+
exec_display = "[blue]ACP[/blue]"
|
|
69
|
+
# For ACP, show the agent (new field or legacy coding_agent)
|
|
70
|
+
provider_agent = role_config.agent_id or role_config.coding_agent
|
|
71
|
+
model = "(agent-managed)"
|
|
72
|
+
else:
|
|
73
|
+
exec_display = "[green]BYOK[/green]"
|
|
74
|
+
provider_agent = role_config.provider or "-"
|
|
75
|
+
model = role_config.model or "-"
|
|
76
|
+
|
|
77
|
+
status = (
|
|
78
|
+
"[green]✅ Enabled[/green]" if role_config.enabled else "[red]❌ Disabled[/red]"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
table.add_row(
|
|
82
|
+
f"{mode_name}.{role_name}",
|
|
83
|
+
mode_name,
|
|
84
|
+
exec_display,
|
|
85
|
+
provider_agent,
|
|
86
|
+
model,
|
|
87
|
+
status,
|
|
88
|
+
)
|
|
89
|
+
elif mode_config.direct_role:
|
|
90
|
+
role_config = mode_config.direct_role
|
|
91
|
+
if enabled_only and not role_config.enabled:
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
exec_mode = role_config.execution_mode
|
|
95
|
+
if exec_mode == "acp":
|
|
96
|
+
exec_display = "[blue]ACP[/blue]"
|
|
97
|
+
provider_agent = role_config.agent_id or role_config.coding_agent
|
|
98
|
+
model = "(agent-managed)"
|
|
99
|
+
else:
|
|
100
|
+
exec_display = "[green]BYOK[/green]"
|
|
101
|
+
provider_agent = role_config.provider or "-"
|
|
102
|
+
model = role_config.model or "-"
|
|
103
|
+
|
|
104
|
+
status = (
|
|
105
|
+
"[green]✅ Enabled[/green]" if role_config.enabled else "[red]❌ Disabled[/red]"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
table.add_row(
|
|
109
|
+
mode_name,
|
|
110
|
+
mode_name,
|
|
111
|
+
exec_display,
|
|
112
|
+
provider_agent,
|
|
113
|
+
model,
|
|
114
|
+
status,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
console.print(table)
|
|
118
|
+
|
|
119
|
+
# Legend
|
|
120
|
+
console.print()
|
|
121
|
+
console.print("[dim]Execution Modes:[/dim]")
|
|
122
|
+
console.print(" [green]BYOK[/green] = Bring Your Own Key (direct LLM API via gateway)")
|
|
123
|
+
console.print(" [blue]ACP[/blue] = Agent Client Protocol (full coding agent)")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@roles.command("info")
|
|
127
|
+
@click.argument("role_path", metavar="MODE.ROLE")
|
|
128
|
+
def role_info(role_path: str):
|
|
129
|
+
"""Show detailed execution information for a role.
|
|
130
|
+
|
|
131
|
+
Examples:
|
|
132
|
+
superqode roles info dev.fullstack
|
|
133
|
+
superqode roles info qe.api_tester
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
# Parse role path
|
|
137
|
+
parts = role_path.split(".", 1)
|
|
138
|
+
mode_name = parts[0]
|
|
139
|
+
role_name = parts[1] if len(parts) > 1 else None
|
|
140
|
+
|
|
141
|
+
config = load_config()
|
|
142
|
+
resolved = resolve_role(mode_name, role_name, config)
|
|
143
|
+
|
|
144
|
+
if not resolved:
|
|
145
|
+
console.print(f"[red]Error: Role '{role_path}' not found or disabled[/red]")
|
|
146
|
+
console.print("\nUse 'superqode roles list' to see available roles.")
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
# Build info panel
|
|
150
|
+
info_lines = []
|
|
151
|
+
|
|
152
|
+
# Basic info
|
|
153
|
+
info_lines.append(f"[bold]Role:[/bold] {role_path}")
|
|
154
|
+
info_lines.append(f"[bold]Description:[/bold] {resolved.description}")
|
|
155
|
+
info_lines.append(f"[bold]Enabled:[/bold] {'Yes' if resolved.enabled else 'No'}")
|
|
156
|
+
info_lines.append("")
|
|
157
|
+
|
|
158
|
+
# Execution mode section
|
|
159
|
+
exec_mode = resolved.execution_mode
|
|
160
|
+
|
|
161
|
+
if exec_mode == "byok":
|
|
162
|
+
info_lines.append("[bold cyan]═══ BYOK MODE (Direct LLM) ═══[/bold cyan]")
|
|
163
|
+
info_lines.append("")
|
|
164
|
+
info_lines.append(f"[bold]Provider:[/bold] {resolved.provider or '(not set)'}")
|
|
165
|
+
info_lines.append(f"[bold]Model:[/bold] {resolved.model or '(not set)'}")
|
|
166
|
+
info_lines.append(f"[bold]Gateway:[/bold] LiteLLM")
|
|
167
|
+
info_lines.append("")
|
|
168
|
+
|
|
169
|
+
# Check provider status
|
|
170
|
+
if resolved.provider:
|
|
171
|
+
provider_def = PROVIDERS.get(resolved.provider)
|
|
172
|
+
if provider_def:
|
|
173
|
+
# Check env vars
|
|
174
|
+
configured = False
|
|
175
|
+
for env_var in provider_def.env_vars:
|
|
176
|
+
if os.environ.get(env_var):
|
|
177
|
+
configured = True
|
|
178
|
+
break
|
|
179
|
+
|
|
180
|
+
if not provider_def.env_vars:
|
|
181
|
+
configured = True # Local provider
|
|
182
|
+
|
|
183
|
+
status = (
|
|
184
|
+
"[green]✅ Configured[/green]" if configured else "[red]❌ Not configured[/red]"
|
|
185
|
+
)
|
|
186
|
+
info_lines.append(f"[bold]Provider Status:[/bold] {status}")
|
|
187
|
+
|
|
188
|
+
if provider_def.env_vars:
|
|
189
|
+
info_lines.append(f"[bold]Required Env:[/bold] {provider_def.env_vars[0]}")
|
|
190
|
+
|
|
191
|
+
if not configured:
|
|
192
|
+
info_lines.append("")
|
|
193
|
+
info_lines.append(
|
|
194
|
+
f'[yellow]To configure: export {provider_def.env_vars[0]}="your-key"[/yellow]'
|
|
195
|
+
)
|
|
196
|
+
info_lines.append(f"[yellow]Get key at: {provider_def.docs_url}[/yellow]")
|
|
197
|
+
else:
|
|
198
|
+
info_lines.append(
|
|
199
|
+
f"[yellow]⚠️ Provider '{resolved.provider}' not in registry[/yellow]"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
info_lines.append("")
|
|
203
|
+
info_lines.append("[bold]Capabilities:[/bold]")
|
|
204
|
+
info_lines.append(" • Chat completion")
|
|
205
|
+
info_lines.append(" • Streaming responses")
|
|
206
|
+
info_lines.append(" • Tool calling (if model supports)")
|
|
207
|
+
info_lines.append("")
|
|
208
|
+
info_lines.append("[bold]Limitations:[/bold]")
|
|
209
|
+
info_lines.append(" • No file editing")
|
|
210
|
+
info_lines.append(" • No shell commands")
|
|
211
|
+
info_lines.append(" • No MCP tools")
|
|
212
|
+
|
|
213
|
+
else: # ACP mode
|
|
214
|
+
info_lines.append("[bold blue]═══ ACP MODE (Coding Agent) ═══[/bold blue]")
|
|
215
|
+
info_lines.append("")
|
|
216
|
+
|
|
217
|
+
agent_id = resolved.agent_id or resolved.coding_agent
|
|
218
|
+
info_lines.append(f"[bold]Agent:[/bold] {agent_id}")
|
|
219
|
+
|
|
220
|
+
# Check agent status
|
|
221
|
+
agent_def = AGENTS.get(agent_id)
|
|
222
|
+
if agent_def:
|
|
223
|
+
status_str = {
|
|
224
|
+
AgentStatus.SUPPORTED: "[green]✅ Supported[/green]",
|
|
225
|
+
AgentStatus.COMING_SOON: "[yellow]⏳ Coming Soon[/yellow]",
|
|
226
|
+
AgentStatus.EXPERIMENTAL: "[blue]🧪 Experimental[/blue]",
|
|
227
|
+
}.get(agent_def.status, "[dim]Unknown[/dim]")
|
|
228
|
+
|
|
229
|
+
info_lines.append(f"[bold]Agent Status:[/bold] {status_str}")
|
|
230
|
+
info_lines.append(f"[bold]Protocol:[/bold] {agent_def.protocol.value.upper()}")
|
|
231
|
+
info_lines.append(f"[bold]Auth:[/bold] {agent_def.auth_info}")
|
|
232
|
+
info_lines.append("")
|
|
233
|
+
|
|
234
|
+
# Show agent's LLM config (new style or legacy)
|
|
235
|
+
if resolved.agent_config:
|
|
236
|
+
info_lines.append("[bold]Agent LLM Config:[/bold]")
|
|
237
|
+
if resolved.agent_config.provider:
|
|
238
|
+
info_lines.append(f" Provider: {resolved.agent_config.provider}")
|
|
239
|
+
if resolved.agent_config.model:
|
|
240
|
+
info_lines.append(f" Model: {resolved.agent_config.model}")
|
|
241
|
+
info_lines.append("")
|
|
242
|
+
elif resolved.provider or resolved.model:
|
|
243
|
+
# Legacy: provider/model specified at role level for ACP agent
|
|
244
|
+
info_lines.append("[bold]Agent LLM Config (legacy):[/bold]")
|
|
245
|
+
if resolved.provider:
|
|
246
|
+
info_lines.append(f" Provider: {resolved.provider}")
|
|
247
|
+
if resolved.model:
|
|
248
|
+
info_lines.append(f" Model: {resolved.model}")
|
|
249
|
+
info_lines.append("")
|
|
250
|
+
|
|
251
|
+
info_lines.append("[bold]Capabilities:[/bold]")
|
|
252
|
+
for cap in agent_def.capabilities:
|
|
253
|
+
info_lines.append(f" • {cap}")
|
|
254
|
+
|
|
255
|
+
if agent_def.status != AgentStatus.SUPPORTED:
|
|
256
|
+
info_lines.append("")
|
|
257
|
+
info_lines.append(f"[yellow]Setup: {agent_def.setup_command}[/yellow]")
|
|
258
|
+
else:
|
|
259
|
+
info_lines.append(f"[yellow]⚠️ Agent '{agent_id}' not in registry[/yellow]")
|
|
260
|
+
|
|
261
|
+
# MCP servers
|
|
262
|
+
if resolved.mcp_servers:
|
|
263
|
+
info_lines.append("")
|
|
264
|
+
info_lines.append("[bold]MCP Servers:[/bold]")
|
|
265
|
+
for server in resolved.mcp_servers:
|
|
266
|
+
info_lines.append(f" • {server}")
|
|
267
|
+
|
|
268
|
+
# Job description
|
|
269
|
+
if resolved.job_description:
|
|
270
|
+
info_lines.append("")
|
|
271
|
+
info_lines.append("[bold]Job Description:[/bold]")
|
|
272
|
+
# Truncate long descriptions
|
|
273
|
+
desc = resolved.job_description.strip()
|
|
274
|
+
if len(desc) > 200:
|
|
275
|
+
desc = desc[:200] + "..."
|
|
276
|
+
info_lines.append(f" {desc}")
|
|
277
|
+
|
|
278
|
+
# Auth info
|
|
279
|
+
info_lines.append("")
|
|
280
|
+
info_lines.append("[bold cyan]═══ SECURITY ═══[/bold cyan]")
|
|
281
|
+
info_lines.append("")
|
|
282
|
+
if exec_mode == "byok":
|
|
283
|
+
info_lines.append("🔒 API key read from YOUR environment variables")
|
|
284
|
+
info_lines.append("🔒 SuperQode NEVER stores your keys")
|
|
285
|
+
info_lines.append("🔒 Data flows: You → SuperQode → LiteLLM → Provider")
|
|
286
|
+
else:
|
|
287
|
+
info_lines.append("🔒 Auth managed by the agent (not SuperQode)")
|
|
288
|
+
info_lines.append("🔒 Agent stores its own credentials")
|
|
289
|
+
info_lines.append("🔒 Data flows: You → SuperQode → Agent → Provider")
|
|
290
|
+
|
|
291
|
+
panel = Panel(
|
|
292
|
+
"\n".join(info_lines),
|
|
293
|
+
title=f"Role: {role_path}",
|
|
294
|
+
border_style="cyan",
|
|
295
|
+
)
|
|
296
|
+
console.print(panel)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@roles.command("check")
|
|
300
|
+
@click.argument("role_path", metavar="MODE.ROLE")
|
|
301
|
+
def role_check(role_path: str):
|
|
302
|
+
"""Check if a role is ready to run (auth configured, etc.)."""
|
|
303
|
+
|
|
304
|
+
# Parse role path
|
|
305
|
+
parts = role_path.split(".", 1)
|
|
306
|
+
mode_name = parts[0]
|
|
307
|
+
role_name = parts[1] if len(parts) > 1 else None
|
|
308
|
+
|
|
309
|
+
config = load_config()
|
|
310
|
+
resolved = resolve_role(mode_name, role_name, config)
|
|
311
|
+
|
|
312
|
+
if not resolved:
|
|
313
|
+
console.print(f"[red]❌ Role '{role_path}' not found or disabled[/red]")
|
|
314
|
+
return
|
|
315
|
+
|
|
316
|
+
console.print(f"Checking role: {role_path}")
|
|
317
|
+
console.print()
|
|
318
|
+
|
|
319
|
+
issues = []
|
|
320
|
+
warnings = []
|
|
321
|
+
|
|
322
|
+
exec_mode = resolved.execution_mode
|
|
323
|
+
|
|
324
|
+
if exec_mode == "byok":
|
|
325
|
+
# Check provider
|
|
326
|
+
if not resolved.provider:
|
|
327
|
+
issues.append("No provider specified")
|
|
328
|
+
else:
|
|
329
|
+
provider_def = PROVIDERS.get(resolved.provider)
|
|
330
|
+
if not provider_def:
|
|
331
|
+
warnings.append(f"Provider '{resolved.provider}' not in registry (may still work)")
|
|
332
|
+
elif provider_def.env_vars:
|
|
333
|
+
configured = False
|
|
334
|
+
for env_var in provider_def.env_vars:
|
|
335
|
+
if os.environ.get(env_var):
|
|
336
|
+
configured = True
|
|
337
|
+
break
|
|
338
|
+
if not configured:
|
|
339
|
+
issues.append(f"API key not set. Set {provider_def.env_vars[0]}")
|
|
340
|
+
|
|
341
|
+
# Check model
|
|
342
|
+
if not resolved.model:
|
|
343
|
+
issues.append("No model specified")
|
|
344
|
+
|
|
345
|
+
else: # ACP
|
|
346
|
+
agent_id = resolved.agent_id or resolved.coding_agent
|
|
347
|
+
agent_def = AGENTS.get(agent_id)
|
|
348
|
+
|
|
349
|
+
if not agent_def:
|
|
350
|
+
warnings.append(f"Agent '{agent_id}' not in registry")
|
|
351
|
+
elif agent_def.status != AgentStatus.SUPPORTED:
|
|
352
|
+
issues.append(
|
|
353
|
+
f"Agent '{agent_id}' is not yet supported (status: {agent_def.status.value})"
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Display results
|
|
357
|
+
if issues:
|
|
358
|
+
console.print("[red]❌ Issues found:[/red]")
|
|
359
|
+
for issue in issues:
|
|
360
|
+
console.print(f" • {issue}")
|
|
361
|
+
console.print()
|
|
362
|
+
|
|
363
|
+
if warnings:
|
|
364
|
+
console.print("[yellow]⚠️ Warnings:[/yellow]")
|
|
365
|
+
for warning in warnings:
|
|
366
|
+
console.print(f" • {warning}")
|
|
367
|
+
console.print()
|
|
368
|
+
|
|
369
|
+
if not issues and not warnings:
|
|
370
|
+
console.print("[green]✅ Role is ready to run![/green]")
|
|
371
|
+
elif not issues:
|
|
372
|
+
console.print("[green]✅ Role should work (with warnings)[/green]")
|
|
373
|
+
else:
|
|
374
|
+
console.print("[red]❌ Role has issues that need to be fixed[/red]")
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# Register with main CLI
|
|
378
|
+
def register_commands(cli):
|
|
379
|
+
"""Register roles commands with the main CLI."""
|
|
380
|
+
cli.add_command(roles)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode Server Commands.
|
|
3
|
+
|
|
4
|
+
Start various SuperQode servers:
|
|
5
|
+
- LSP server for IDE integration
|
|
6
|
+
- Web server for browser-based TUI
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
|
|
17
|
+
from superqode.enterprise import require_enterprise
|
|
18
|
+
|
|
19
|
+
console = Console()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.group()
|
|
23
|
+
def serve():
|
|
24
|
+
"""Server commands for IDE and web integration."""
|
|
25
|
+
if not require_enterprise("Server integrations"):
|
|
26
|
+
raise SystemExit(1)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@serve.command("lsp")
|
|
30
|
+
@click.option(
|
|
31
|
+
"--transport",
|
|
32
|
+
"-t",
|
|
33
|
+
type=click.Choice(["stdio", "tcp"]),
|
|
34
|
+
default="stdio",
|
|
35
|
+
help="Transport mode: stdio (default) for editors, tcp for debugging",
|
|
36
|
+
)
|
|
37
|
+
@click.option("--port", "-p", default=9000, help="Port for TCP transport (default: 9000)")
|
|
38
|
+
@click.option("--project", type=click.Path(exists=True), default=".", help="Project root directory")
|
|
39
|
+
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose logging")
|
|
40
|
+
def serve_lsp(transport: str, port: int, project: str, verbose: bool):
|
|
41
|
+
"""Start the LSP server for IDE integration.
|
|
42
|
+
|
|
43
|
+
The LSP server exposes QE findings as diagnostics in your IDE.
|
|
44
|
+
Supports VSCode, Neovim, and other LSP-compatible editors.
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
|
|
48
|
+
superqode serve lsp # Start in stdio mode (for editors)
|
|
49
|
+
|
|
50
|
+
superqode serve lsp -t tcp -p 9000 # Start in TCP mode (for debugging)
|
|
51
|
+
|
|
52
|
+
VSCode Setup:
|
|
53
|
+
1. Install the SuperQode VSCode extension
|
|
54
|
+
2. The extension will automatically connect to the LSP server
|
|
55
|
+
|
|
56
|
+
Neovim Setup (with nvim-lspconfig):
|
|
57
|
+
require('lspconfig.configs').superqode = {
|
|
58
|
+
default_config = {
|
|
59
|
+
cmd = { 'superqode', 'serve', 'lsp' },
|
|
60
|
+
filetypes = { '*' },
|
|
61
|
+
root_dir = function(fname)
|
|
62
|
+
return vim.fn.getcwd()
|
|
63
|
+
end,
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
require('lspconfig').superqode.setup{}
|
|
67
|
+
"""
|
|
68
|
+
import logging
|
|
69
|
+
|
|
70
|
+
if verbose:
|
|
71
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
72
|
+
else:
|
|
73
|
+
logging.basicConfig(level=logging.INFO)
|
|
74
|
+
|
|
75
|
+
from superqode.server import start_lsp_server
|
|
76
|
+
|
|
77
|
+
project_root = Path(project).resolve()
|
|
78
|
+
|
|
79
|
+
if transport == "tcp":
|
|
80
|
+
console.print(f"[cyan]Starting SuperQode LSP server on port {port}[/cyan]")
|
|
81
|
+
console.print("[dim]Connect your editor to localhost:{port}[/dim]")
|
|
82
|
+
else:
|
|
83
|
+
# stdio mode - don't print to stdout as it interferes with LSP
|
|
84
|
+
import sys
|
|
85
|
+
|
|
86
|
+
sys.stderr.write("SuperQode LSP server starting (stdio mode)\n")
|
|
87
|
+
|
|
88
|
+
start_lsp_server(
|
|
89
|
+
project_root=project_root,
|
|
90
|
+
transport=transport,
|
|
91
|
+
port=port,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@serve.command("web")
|
|
96
|
+
@click.option("--port", "-p", default=8080, help="Port for web server (default: 8080)")
|
|
97
|
+
@click.option("--host", "-h", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)")
|
|
98
|
+
@click.option("--project", type=click.Path(exists=True), default=".", help="Project root directory")
|
|
99
|
+
@click.option("--no-open", is_flag=True, help="Don't open browser automatically")
|
|
100
|
+
def serve_web(port: int, host: str, project: str, no_open: bool):
|
|
101
|
+
"""Start the web server for browser-based TUI.
|
|
102
|
+
|
|
103
|
+
Run SuperQode's TUI interface in your web browser.
|
|
104
|
+
|
|
105
|
+
Examples:
|
|
106
|
+
|
|
107
|
+
superqode serve web # Start on localhost:8080
|
|
108
|
+
|
|
109
|
+
superqode serve web -p 3000 # Use custom port
|
|
110
|
+
|
|
111
|
+
superqode serve web -h 0.0.0.0 # Allow external connections
|
|
112
|
+
"""
|
|
113
|
+
from superqode.server import start_server, WebServerConfig
|
|
114
|
+
|
|
115
|
+
project_root = Path(project).resolve()
|
|
116
|
+
|
|
117
|
+
console.print(f"[cyan]Starting SuperQode web server on http://{host}:{port}[/cyan]")
|
|
118
|
+
|
|
119
|
+
config = WebServerConfig(
|
|
120
|
+
host=host,
|
|
121
|
+
port=port,
|
|
122
|
+
project_root=project_root,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
start_server(config, open_browser=not no_open)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@serve.command("status")
|
|
129
|
+
@click.option("--project", type=click.Path(exists=True), default=".", help="Project root directory")
|
|
130
|
+
def serve_status(project: str):
|
|
131
|
+
"""Show status of running servers."""
|
|
132
|
+
import socket
|
|
133
|
+
|
|
134
|
+
project_root = Path(project).resolve()
|
|
135
|
+
|
|
136
|
+
console.print()
|
|
137
|
+
console.print("[bold]SuperQode Server Status[/bold]")
|
|
138
|
+
console.print()
|
|
139
|
+
|
|
140
|
+
# Check LSP TCP port
|
|
141
|
+
lsp_port = 9000
|
|
142
|
+
lsp_running = False
|
|
143
|
+
try:
|
|
144
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
145
|
+
result = sock.connect_ex(("127.0.0.1", lsp_port))
|
|
146
|
+
lsp_running = result == 0
|
|
147
|
+
sock.close()
|
|
148
|
+
except Exception:
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
if lsp_running:
|
|
152
|
+
console.print(f"[green]LSP Server:[/green] Running on port {lsp_port}")
|
|
153
|
+
else:
|
|
154
|
+
console.print(f"[dim]LSP Server:[/dim] Not running (stdio mode doesn't show here)")
|
|
155
|
+
|
|
156
|
+
# Check web server port
|
|
157
|
+
web_port = 8080
|
|
158
|
+
web_running = False
|
|
159
|
+
try:
|
|
160
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
161
|
+
result = sock.connect_ex(("127.0.0.1", web_port))
|
|
162
|
+
web_running = result == 0
|
|
163
|
+
sock.close()
|
|
164
|
+
except Exception:
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
if web_running:
|
|
168
|
+
console.print(f"[green]Web Server:[/green] Running on port {web_port}")
|
|
169
|
+
else:
|
|
170
|
+
console.print(f"[dim]Web Server:[/dim] Not running")
|
|
171
|
+
|
|
172
|
+
console.print()
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Suggestions command - Review verified fixes from QE sessions.
|
|
3
|
+
|
|
4
|
+
This command allows users to:
|
|
5
|
+
- List all verified fixes from QE sessions
|
|
6
|
+
- View suggestion details and evidence
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
from rich.table import Table
|
|
16
|
+
from rich.text import Text
|
|
17
|
+
|
|
18
|
+
from superqode.enterprise import require_enterprise
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@click.group()
|
|
22
|
+
def suggestions():
|
|
23
|
+
"""Review verified fixes from QE sessions."""
|
|
24
|
+
if not require_enterprise("QE suggestions"):
|
|
25
|
+
raise SystemExit(1)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
console = Console()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_verified_fixes(project_root: Path) -> list:
|
|
32
|
+
"""Load verified fixes from QIR files."""
|
|
33
|
+
fixes = []
|
|
34
|
+
qr_dir = project_root / ".superqode" / "qe-artifacts" / "reports"
|
|
35
|
+
|
|
36
|
+
if not qr_dir.exists():
|
|
37
|
+
return fixes
|
|
38
|
+
|
|
39
|
+
# Look for recent QIR files
|
|
40
|
+
json_files = list(qr_dir.glob("qr-*.json"))
|
|
41
|
+
if not json_files:
|
|
42
|
+
return fixes
|
|
43
|
+
|
|
44
|
+
# Load the most recent QIR
|
|
45
|
+
latest_qr = max(json_files, key=lambda f: f.stat().st_mtime)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
with open(latest_qr) as f:
|
|
49
|
+
qir_data = json.load(f)
|
|
50
|
+
|
|
51
|
+
# Extract findings with suggestions
|
|
52
|
+
findings = qir_data.get("findings", [])
|
|
53
|
+
for finding in findings:
|
|
54
|
+
if finding.get("suggested_fix"):
|
|
55
|
+
fixes.append(
|
|
56
|
+
{
|
|
57
|
+
"id": finding.get("id", "unknown"),
|
|
58
|
+
"title": finding.get("title", ""),
|
|
59
|
+
"severity": finding.get("severity", "info"),
|
|
60
|
+
"suggested_fix": finding.get("suggested_fix", ""),
|
|
61
|
+
"confidence": finding.get("confidence", 0.5),
|
|
62
|
+
"is_improvement": finding.get("confidence", 0) > 0.7,
|
|
63
|
+
"fix_verified": True, # Assume verified if in QIR
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
console.print(f"[red]Error loading QIR: {e}[/red]")
|
|
69
|
+
|
|
70
|
+
return fixes
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_artifacts_dir(project_root: Path) -> Path:
|
|
74
|
+
"""Get the QE artifacts directory."""
|
|
75
|
+
return project_root / ".superqode" / "qe-artifacts"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@suggestions.command("list")
|
|
79
|
+
@click.argument("project_root", type=click.Path(exists=True), default=".")
|
|
80
|
+
@click.option(
|
|
81
|
+
"--all", "-a", "show_all", is_flag=True, help="Show all suggestions, not just improvements"
|
|
82
|
+
)
|
|
83
|
+
def list_suggestions(project_root, show_all):
|
|
84
|
+
"""List all verified fix suggestions from QE sessions."""
|
|
85
|
+
|
|
86
|
+
project_path = Path(project_root)
|
|
87
|
+
fixes = load_verified_fixes(project_path)
|
|
88
|
+
|
|
89
|
+
if not fixes:
|
|
90
|
+
console.print("[yellow]No verified fixes found.[/yellow]")
|
|
91
|
+
console.print(
|
|
92
|
+
"[dim]Run 'superqe run . --mode deep --allow-suggestions' to generate fix suggestions.[/dim]"
|
|
93
|
+
)
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
# Filter to improvements only by default
|
|
97
|
+
if not show_all:
|
|
98
|
+
fixes = [f for f in fixes if f.get("is_improvement", False)]
|
|
99
|
+
|
|
100
|
+
if not fixes:
|
|
101
|
+
console.print(
|
|
102
|
+
"[green]All suggestions have been processed or none passed verification.[/green]"
|
|
103
|
+
)
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
# Display table
|
|
107
|
+
table = Table(title="Verified Fix Suggestions", show_header=True, header_style="bold")
|
|
108
|
+
table.add_column("#", style="dim", width=3)
|
|
109
|
+
table.add_column("Finding", style="cyan")
|
|
110
|
+
table.add_column("Status", justify="center")
|
|
111
|
+
table.add_column("Confidence", justify="right")
|
|
112
|
+
|
|
113
|
+
for i, fix in enumerate(fixes, 1):
|
|
114
|
+
status = "✅ Verified" if fix.get("fix_verified") else "❌ Failed"
|
|
115
|
+
improvement = "⬆️" if fix.get("is_improvement") else "➖"
|
|
116
|
+
|
|
117
|
+
table.add_row(
|
|
118
|
+
str(i),
|
|
119
|
+
fix.get("finding_title", fix.get("title", "Unknown"))[:40],
|
|
120
|
+
f"{status} {improvement}",
|
|
121
|
+
f"{fix.get('fix_confidence', fix.get('confidence', 0)) * 100:.0f}%",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
console.print(table)
|
|
125
|
+
console.print()
|
|
126
|
+
console.print(f"[dim]Total: {len(fixes)} verified fix suggestions[/dim]")
|
|
127
|
+
console.print(f"[dim]Use 'superqe logs' to see detailed agent work logs[/dim]")
|