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,180 @@
1
+ """
2
+ Agent CLI commands for SuperQode.
3
+
4
+ Commands for listing and showing ACP/External agents.
5
+ """
6
+
7
+ import click
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+ from rich.panel import Panel
11
+
12
+ from ..agents.registry import (
13
+ AGENTS,
14
+ AgentProtocol,
15
+ AgentStatus,
16
+ get_supported_agents,
17
+ get_acp_agents,
18
+ get_external_agents,
19
+ )
20
+
21
+
22
+ console = Console()
23
+
24
+
25
+ @click.group()
26
+ def agents():
27
+ """Manage coding agents (ACP mode)."""
28
+ pass
29
+
30
+
31
+ @agents.command("list")
32
+ @click.option(
33
+ "--protocol",
34
+ type=click.Choice(["acp", "external"]),
35
+ help="Filter by protocol",
36
+ )
37
+ @click.option(
38
+ "--supported",
39
+ is_flag=True,
40
+ help="Show only supported agents",
41
+ )
42
+ def list_agents(protocol: str, supported: bool):
43
+ """List available coding agents."""
44
+
45
+ # Filter agents
46
+ filtered = dict(AGENTS)
47
+
48
+ if protocol:
49
+ proto = AgentProtocol.ACP if protocol == "acp" else AgentProtocol.EXTERNAL
50
+ filtered = {k: v for k, v in filtered.items() if v.protocol == proto}
51
+
52
+ if supported:
53
+ filtered = {k: v for k, v in filtered.items() if v.status == AgentStatus.SUPPORTED}
54
+
55
+ # Build table
56
+ table = Table(title="Coding Agents", show_header=True, header_style="bold cyan")
57
+ table.add_column("Agent", style="white")
58
+ table.add_column("Name", style="white")
59
+ table.add_column("Protocol", style="dim")
60
+ table.add_column("Status", style="white")
61
+ table.add_column("Description", style="dim", max_width=40)
62
+
63
+ # Sort by status (supported first) then name
64
+ sorted_agents = sorted(filtered.items(), key=lambda x: (x[1].status.value, x[0]))
65
+
66
+ for agent_id, agent_def in sorted_agents:
67
+ status_map = {
68
+ AgentStatus.SUPPORTED: "[green]✅ Supported[/green]",
69
+ AgentStatus.COMING_SOON: "[yellow]🔜 Coming Soon[/yellow]",
70
+ AgentStatus.EXPERIMENTAL: "[blue]🧪 Experimental[/blue]",
71
+ }
72
+ status = status_map.get(agent_def.status, agent_def.status.value)
73
+
74
+ protocol_str = agent_def.protocol.value.upper()
75
+
76
+ table.add_row(
77
+ agent_id,
78
+ agent_def.name,
79
+ protocol_str,
80
+ status,
81
+ agent_def.description[:40] + "..."
82
+ if len(agent_def.description) > 40
83
+ else agent_def.description,
84
+ )
85
+
86
+ console.print(table)
87
+
88
+ # Summary
89
+ supported_count = sum(1 for v in filtered.values() if v.status == AgentStatus.SUPPORTED)
90
+ console.print(f"\n[dim]Total: {len(filtered)} agents, {supported_count} supported[/dim]")
91
+
92
+
93
+ @agents.command("show")
94
+ @click.argument("agent_id")
95
+ def show_agent(agent_id: str):
96
+ """Show details for a specific agent."""
97
+
98
+ agent_def = AGENTS.get(agent_id)
99
+
100
+ if not agent_def:
101
+ console.print(f"[red]Error: Agent '{agent_id}' not found[/red]")
102
+ console.print("\nAvailable agents:")
103
+ for aid in sorted(AGENTS.keys()):
104
+ console.print(f" • {aid}")
105
+ return
106
+
107
+ # Build info panel
108
+ status_map = {
109
+ AgentStatus.SUPPORTED: "[green]✅ Supported[/green]",
110
+ AgentStatus.COMING_SOON: "[yellow]🔜 Coming Soon[/yellow]",
111
+ AgentStatus.EXPERIMENTAL: "[blue]🧪 Experimental[/blue]",
112
+ }
113
+ status = status_map.get(agent_def.status, agent_def.status.value)
114
+
115
+ info_lines = [
116
+ f"[bold]Agent:[/bold] {agent_def.name}",
117
+ f"[bold]ID:[/bold] {agent_id}",
118
+ f"[bold]Protocol:[/bold] {agent_def.protocol.value.upper()}",
119
+ f"[bold]Status:[/bold] {status}",
120
+ f"[bold]Connection:[/bold] {agent_def.connection_type}",
121
+ "",
122
+ f"[bold]Description:[/bold]",
123
+ f" {agent_def.description}",
124
+ "",
125
+ ]
126
+
127
+ # Capabilities
128
+ if agent_def.capabilities:
129
+ info_lines.append("[bold]Capabilities:[/bold]")
130
+ for cap in agent_def.capabilities:
131
+ info_lines.append(f" • {cap}")
132
+ info_lines.append("")
133
+
134
+ # Auth info
135
+ info_lines.append("[bold]Authentication:[/bold]")
136
+ info_lines.append(f" {agent_def.auth_info}")
137
+ info_lines.append("")
138
+
139
+ # Setup
140
+ info_lines.append("[bold]Setup:[/bold]")
141
+ info_lines.append(f" {agent_def.setup_command}")
142
+ info_lines.append("")
143
+
144
+ # Command
145
+ if agent_def.command:
146
+ info_lines.append(f"[bold]Command:[/bold] {agent_def.command}")
147
+ info_lines.append("")
148
+
149
+ # Docs
150
+ info_lines.append(f"[bold]Documentation:[/bold] {agent_def.docs_url}")
151
+
152
+ panel = Panel(
153
+ "\n".join(info_lines),
154
+ title=f"Agent: {agent_def.name}",
155
+ border_style="cyan",
156
+ )
157
+ console.print(panel)
158
+
159
+ # Usage example
160
+ if agent_def.status == AgentStatus.SUPPORTED:
161
+ console.print("\n[bold]Usage in superqode.yaml:[/bold]")
162
+ console.print(f"""
163
+ [dim]team:
164
+ dev:
165
+ roles:
166
+ my-role:
167
+ mode: "acp"
168
+ agent: "{agent_id}"
169
+ agent_config:
170
+ provider: "anthropic"
171
+ model: "claude-sonnet-4-20250514"
172
+ job_description: |
173
+ Your job description here...[/dim]
174
+ """)
175
+
176
+
177
+ # Register with main CLI
178
+ def register_commands(cli):
179
+ """Register agent commands with the main CLI."""
180
+ cli.add_command(agents)
@@ -0,0 +1,278 @@
1
+ """
2
+ Auth CLI commands for SuperQode.
3
+
4
+ Commands for showing authentication information and security details.
5
+ SuperQode NEVER stores API keys - this shows where keys are stored
6
+ and who controls them.
7
+ """
8
+
9
+ import os
10
+ from pathlib import Path
11
+
12
+ import click
13
+ from rich.console import Console
14
+ from rich.table import Table
15
+ from rich.panel import Panel
16
+
17
+ from ..providers.registry import PROVIDERS, ProviderCategory
18
+ from ..agents.registry import AGENTS, AgentStatus
19
+
20
+
21
+ console = Console()
22
+
23
+
24
+ @click.group()
25
+ def auth():
26
+ """Show authentication and security information."""
27
+ pass
28
+
29
+
30
+ @auth.command("info")
31
+ def auth_info():
32
+ """Show comprehensive auth information."""
33
+
34
+ # Header
35
+ console.print(
36
+ Panel(
37
+ "[bold]🔒 SECURITY PRINCIPLE:[/bold] SuperQode [bold red]NEVER[/bold red] stores your API keys.\n\n"
38
+ "All credentials are read from YOUR environment at runtime.\n"
39
+ "You control where and how your keys are stored.",
40
+ title="SuperQode Auth Information",
41
+ border_style="cyan",
42
+ )
43
+ )
44
+
45
+ console.print()
46
+
47
+ # BYOK Section
48
+ console.print("[bold cyan]═══ BYOK MODE (Direct LLM) ═══[/bold cyan]")
49
+ console.print()
50
+ console.print("Your API keys are read from YOUR environment variables:")
51
+ console.print()
52
+
53
+ # Build provider status table
54
+ table = Table(show_header=True, header_style="bold")
55
+ table.add_column("Provider", style="white")
56
+ table.add_column("Env Variable", style="dim")
57
+ table.add_column("Status", style="white")
58
+ table.add_column("Source", style="dim")
59
+
60
+ # Check common providers
61
+ priority_providers = [
62
+ "anthropic",
63
+ "openai",
64
+ "google",
65
+ "xai",
66
+ "deepseek",
67
+ "groq",
68
+ "openrouter",
69
+ "ollama",
70
+ "zhipu",
71
+ "alibaba",
72
+ ]
73
+
74
+ for provider_id in priority_providers:
75
+ provider_def = PROVIDERS.get(provider_id)
76
+ if not provider_def:
77
+ continue
78
+
79
+ # Check env vars
80
+ configured = False
81
+ configured_var = None
82
+
83
+ for env_var in provider_def.env_vars:
84
+ if os.environ.get(env_var):
85
+ configured = True
86
+ configured_var = env_var
87
+ break
88
+
89
+ if not provider_def.env_vars:
90
+ # Local provider
91
+ status = "[blue]🏠 Local[/blue]"
92
+ source = provider_def.default_base_url or "localhost"
93
+ elif configured:
94
+ status = "[green]✅ Set[/green]"
95
+ source = _detect_env_source(configured_var)
96
+ else:
97
+ status = "[red]❌ Not set[/red]"
98
+ source = "-"
99
+
100
+ env_var_display = provider_def.env_vars[0] if provider_def.env_vars else "(none)"
101
+
102
+ table.add_row(
103
+ provider_id,
104
+ env_var_display,
105
+ status,
106
+ source,
107
+ )
108
+
109
+ console.print(table)
110
+ console.print()
111
+ console.print("[dim]💡 Keys are read at runtime, never stored by SuperQode[/dim]")
112
+ console.print()
113
+
114
+ # ACP Section
115
+ console.print("[bold cyan]═══ ACP MODE (Coding Agents) ═══[/bold cyan]")
116
+ console.print()
117
+ console.print("Agent authentication is managed by each agent, not SuperQode:")
118
+ console.print()
119
+
120
+ # Build agent status table
121
+ agent_table = Table(show_header=True, header_style="bold")
122
+ agent_table.add_column("Agent", style="white")
123
+ agent_table.add_column("Auth Location", style="dim")
124
+ agent_table.add_column("Status", style="white")
125
+
126
+ for agent_id, agent_def in AGENTS.items():
127
+ if agent_def.status != AgentStatus.SUPPORTED:
128
+ continue
129
+
130
+ # Check if agent auth exists
131
+ auth_exists = _check_agent_auth(agent_id)
132
+ status = "[green]✅ Configured[/green]" if auth_exists else "[yellow]⚠️ Check agent[/yellow]"
133
+
134
+ agent_table.add_row(
135
+ agent_id,
136
+ agent_def.auth_info,
137
+ status,
138
+ )
139
+
140
+ console.print(agent_table)
141
+ console.print()
142
+ console.print("[dim]💡 Agent auth is managed by the agent itself, not SuperQode[/dim]")
143
+ console.print("[dim]💡 Run the agent directly to configure: e.g., 'opencode' → /connect[/dim]")
144
+ console.print()
145
+
146
+ # Data Flow Section
147
+ console.print("[bold cyan]═══ DATA FLOW ═══[/bold cyan]")
148
+ console.print()
149
+ console.print("[bold]BYOK:[/bold] You → SuperQode → LiteLLM → Provider API")
150
+ console.print("[bold]ACP:[/bold] You → SuperQode → Agent (e.g., opencode) → Provider API")
151
+ console.print()
152
+ console.print("[dim]SuperQode is a pass-through orchestrator. Your data goes directly[/dim]")
153
+ console.print("[dim]to the LLM provider or agent. We don't intercept or store anything.[/dim]")
154
+
155
+
156
+ @auth.command("check")
157
+ @click.argument("provider_or_agent")
158
+ def auth_check(provider_or_agent: str):
159
+ """Check auth status for a specific provider or agent."""
160
+
161
+ # Check if it's a provider
162
+ provider_def = PROVIDERS.get(provider_or_agent)
163
+ if provider_def:
164
+ _check_provider_auth(provider_or_agent, provider_def)
165
+ return
166
+
167
+ # Check if it's an agent
168
+ agent_def = AGENTS.get(provider_or_agent)
169
+ if agent_def:
170
+ _check_agent_auth_detailed(provider_or_agent, agent_def)
171
+ return
172
+
173
+ console.print(f"[red]Error: '{provider_or_agent}' not found as provider or agent[/red]")
174
+ console.print(
175
+ "\nUse 'superqode providers list' or 'superqode agents list' to see available options."
176
+ )
177
+
178
+
179
+ def _detect_env_source(env_var: str) -> str:
180
+ """Try to detect where an env var is set."""
181
+ # This is a best-effort detection
182
+ home = Path.home()
183
+
184
+ # Check common shell config files
185
+ shell_files = [
186
+ home / ".zshrc",
187
+ home / ".bashrc",
188
+ home / ".bash_profile",
189
+ home / ".profile",
190
+ ]
191
+
192
+ for shell_file in shell_files:
193
+ if shell_file.exists():
194
+ try:
195
+ content = shell_file.read_text()
196
+ if env_var in content:
197
+ return f"~/{shell_file.name}"
198
+ except Exception:
199
+ pass
200
+
201
+ # Check .env file in current directory
202
+ env_file = Path.cwd() / ".env"
203
+ if env_file.exists():
204
+ try:
205
+ content = env_file.read_text()
206
+ if env_var in content:
207
+ return ".env"
208
+ except Exception:
209
+ pass
210
+
211
+ return "environment"
212
+
213
+
214
+ def _check_agent_auth(agent_id: str) -> bool:
215
+ """Check if agent auth exists."""
216
+ if agent_id == "opencode":
217
+ auth_file = Path.home() / ".local" / "share" / "opencode" / "auth.json"
218
+ return auth_file.exists()
219
+ return False
220
+
221
+
222
+ def _check_provider_auth(provider_id: str, provider_def):
223
+ """Check and display provider auth status."""
224
+ console.print(f"\n[bold]Provider: {provider_def.name}[/bold]")
225
+ console.print()
226
+
227
+ if not provider_def.env_vars:
228
+ console.print("[blue]🏠 Local provider - no API key required[/blue]")
229
+ if provider_def.default_base_url:
230
+ console.print(f"Default URL: {provider_def.default_base_url}")
231
+ return
232
+
233
+ configured = False
234
+ for env_var in provider_def.env_vars:
235
+ value = os.environ.get(env_var)
236
+ if value:
237
+ configured = True
238
+ # Mask the key
239
+ masked = value[:8] + "..." + value[-4:] if len(value) > 12 else "***"
240
+ source = _detect_env_source(env_var)
241
+ console.print(f"[green]✅ {env_var}[/green] = {masked}")
242
+ console.print(f" Source: {source}")
243
+ else:
244
+ console.print(f"[red]❌ {env_var}[/red] = (not set)")
245
+
246
+ if not configured:
247
+ console.print()
248
+ console.print("[yellow]To configure:[/yellow]")
249
+ console.print(f' export {provider_def.env_vars[0]}="your-api-key"')
250
+ console.print(f"\n Get your key at: {provider_def.docs_url}")
251
+
252
+
253
+ def _check_agent_auth_detailed(agent_id: str, agent_def):
254
+ """Check and display agent auth status."""
255
+ console.print(f"\n[bold]Agent: {agent_def.name}[/bold]")
256
+ console.print()
257
+
258
+ console.print(f"[bold]Auth managed by:[/bold] {agent_def.name} (not SuperQode)")
259
+ console.print(f"[bold]Auth location:[/bold] {agent_def.auth_info}")
260
+ console.print()
261
+
262
+ if agent_id == "opencode":
263
+ auth_file = Path.home() / ".local" / "share" / "opencode" / "auth.json"
264
+ if auth_file.exists():
265
+ console.print(f"[green]✅ Auth file exists:[/green] {auth_file}")
266
+ else:
267
+ console.print(f"[yellow]⚠️ Auth file not found:[/yellow] {auth_file}")
268
+ console.print()
269
+ console.print("[yellow]To configure:[/yellow]")
270
+ console.print(f" {agent_def.setup_command}")
271
+ else:
272
+ console.print(f"[dim]Setup: {agent_def.setup_command}[/dim]")
273
+
274
+
275
+ # Register with main CLI
276
+ def register_commands(cli):
277
+ """Register auth commands with the main CLI."""
278
+ cli.add_command(auth)