superqode 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,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]")