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,965 @@
1
+ """ACP (Agent-Client Protocol) commands for SuperQode."""
2
+
3
+ import asyncio
4
+ import os
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING
8
+
9
+ from rich.console import Console
10
+ from rich.panel import Panel
11
+ from rich.table import Table
12
+ from rich.text import Text
13
+
14
+ from superqode.agents.client import ACPAgentManager
15
+ from superqode.agents.discovery import get_agent_by_identity, get_agent_by_short_name, read_agents
16
+
17
+ if TYPE_CHECKING:
18
+ from superqode.agents.schema import Agent
19
+
20
+ _console = Console()
21
+
22
+
23
+ def check_agent_installed(agent: "Agent") -> bool:
24
+ """Check if an agent is installed on the system."""
25
+ import shutil
26
+
27
+ run_command = agent.get("run_command", {}).get("*", "")
28
+ if not run_command:
29
+ return False
30
+
31
+ # Extract the command name (first part before any spaces or arguments)
32
+ cmd_name = run_command.split()[0]
33
+
34
+ # Check if command exists in PATH
35
+ return shutil.which(cmd_name) is not None
36
+
37
+
38
+ def create_agent_card(
39
+ agent: "Agent", is_enabled: bool = False, is_installed: bool | None = None
40
+ ) -> Panel:
41
+ """Create a beautiful agent card."""
42
+ # Check installation status if not provided
43
+ if is_installed is None:
44
+ is_installed = check_agent_installed(agent)
45
+
46
+ # Status indicators
47
+ status_icon = "✅" if is_enabled else "⏳" if is_installed else "📦"
48
+ status_text = "Enabled" if is_enabled else "Ready" if is_installed else "Available"
49
+ status_color = "green" if is_enabled else "yellow" if is_installed else "dim"
50
+
51
+ # Agent info
52
+ name = agent["name"]
53
+ short_name = agent["short_name"]
54
+ description = agent["description"]
55
+ author = agent["author_name"]
56
+ agent_type = agent["type"]
57
+
58
+ # Type badge
59
+ type_badge = f"[bold white on blue] {agent_type.upper()} [/bold white on blue]"
60
+
61
+ content = f"""[bold cyan]{name}[/bold cyan] [dim]({short_name})[/dim]
62
+ {type_badge}
63
+
64
+ [white]{description}[/white]
65
+
66
+ [dim]By {author}[/dim]
67
+ [{status_color}]{status_icon} {status_text}[/{status_color}]"""
68
+
69
+ border_style = "bright_green" if is_enabled else "cyan" if is_installed else "dim"
70
+
71
+ return Panel.fit(
72
+ content,
73
+ border_style=border_style,
74
+ padding=(1, 2),
75
+ title=f"[bold]{short_name}[/bold]",
76
+ title_align="center",
77
+ )
78
+
79
+
80
+ def show_agents_store() -> None:
81
+ """Show the beautiful Agent Store interface."""
82
+ import asyncio
83
+ from superqode.config import load_config
84
+ from superqode.agents.registry import get_all_acp_agents
85
+
86
+ try:
87
+ agents = asyncio.run(get_all_acp_agents())
88
+ except Exception as e:
89
+ _console.print(f"[red]Error loading agents: {e}[/red]")
90
+ return
91
+
92
+ if not agents:
93
+ _console.print("[yellow]No ACP agents found. Agent configurations may be missing.[/yellow]")
94
+ return
95
+
96
+ # Get current configuration
97
+ config = load_config()
98
+ config_agents = (
99
+ getattr(config, "agents", {}).get("acp", {}) if hasattr(config, "agents") else {}
100
+ )
101
+
102
+ # Count installed vs not installed
103
+ installed_count = sum(1 for agent_data in agents.values() if check_agent_installed(agent_data))
104
+ not_installed_count = len(agents) - installed_count
105
+
106
+ # Header
107
+ header = Panel.fit(
108
+ "[bold bright_blue]🛍️ SuperQode Agent Store[/bold bright_blue]\n"
109
+ "[dim]Discover and install AI coding agents for your development team[/dim]\n\n"
110
+ f"[cyan]📊 {len(agents)} agents available[/cyan] | "
111
+ f"[green]✓ {installed_count} installed[/green] | "
112
+ f"[yellow]○ {not_installed_count} not installed[/yellow]",
113
+ border_style="bright_blue",
114
+ padding=(1, 2),
115
+ )
116
+
117
+ _console.print(header)
118
+ _console.print()
119
+
120
+ # Create agent cards in a grid layout (3 per row)
121
+ agent_cards = []
122
+ for agent_id, agent_data in agents.items():
123
+ is_enabled = config_agents.get(agent_id, {}).get("enabled", False)
124
+ is_installed = check_agent_installed(agent_data)
125
+ card = create_agent_card(agent_data, is_enabled, is_installed)
126
+ agent_cards.append(card)
127
+
128
+ # Display in rows of 3
129
+ from rich.columns import Columns
130
+
131
+ for i in range(0, len(agent_cards), 3):
132
+ row_cards = agent_cards[i : i + 3]
133
+ if len(row_cards) == 1:
134
+ _console.print(row_cards[0])
135
+ else:
136
+ _console.print(Columns(row_cards, equal=True, expand=True))
137
+ _console.print()
138
+
139
+ # Footer with commands
140
+ footer = Panel.fit(
141
+ "[bold cyan]🚀 Quick Commands:[/bold cyan]\n\n"
142
+ "[green]superqode agents show <agent>[/green] View detailed agent info\n"
143
+ "[green]superqode agents install <agent>[/green] Install agent on your system\n"
144
+ "[green]superqode agents connect <agent> [model][/green] Connect with specific model\n\n"
145
+ "[bold cyan]💻 Interactive Commands:[/bold cyan]\n"
146
+ "[yellow]:agents[/yellow] Browse this marketplace\n"
147
+ "[yellow]:agents install <agent>[/yellow] Install directly from here\n"
148
+ "[yellow]:agents connect <agent> [model][/yellow] Connect with model\n"
149
+ "[yellow]:agent <command>[/yellow] Same as :agents (singular)\n\n"
150
+ "[dim]💡 Configure agents in your superqode.yaml to enable them in your team[/dim]",
151
+ border_style="cyan",
152
+ padding=(1, 2),
153
+ )
154
+
155
+ _console.print(footer)
156
+
157
+
158
+ def show_agents_list() -> None:
159
+ """Show a list of available ACP agents with installation status."""
160
+ import asyncio
161
+ from superqode.config import load_config
162
+ from superqode.agents.registry import get_all_acp_agents, get_agent_installation_info
163
+
164
+ try:
165
+ agents = asyncio.run(get_all_acp_agents())
166
+ except Exception as e:
167
+ _console.print(f"[red]Error loading agents: {e}[/red]")
168
+ return
169
+
170
+ if not agents:
171
+ _console.print("[yellow]No ACP agents found. Agent configurations may be missing.[/yellow]")
172
+ return
173
+
174
+ # Get current configuration
175
+ config = load_config()
176
+ config_agents = (
177
+ getattr(config, "agents", {}).get("acp", {}) if hasattr(config, "agents") else {}
178
+ )
179
+
180
+ _console.print()
181
+ _console.print("[bold bright_blue]🤖 Available ACP Coding Agents[/bold bright_blue]")
182
+ _console.print()
183
+
184
+ # Separate agents by installation status
185
+ installed_agents = []
186
+ not_installed_agents = []
187
+
188
+ for agent_id, agent_data in agents.items():
189
+ is_installed = check_agent_installed(agent_data)
190
+ if is_installed:
191
+ installed_agents.append((agent_id, agent_data))
192
+ else:
193
+ not_installed_agents.append((agent_id, agent_data))
194
+
195
+ # Show installed agents first
196
+ if installed_agents:
197
+ _console.print("[bold green]✓ Installed Agents[/bold green]")
198
+ _console.print()
199
+
200
+ table = Table(show_header=True, header_style="bold green")
201
+ table.add_column("Name", style="cyan", no_wrap=True)
202
+ table.add_column("Short Name", style="green")
203
+ table.add_column("Description", style="white", max_width=50)
204
+ table.add_column("Author", style="yellow")
205
+ table.add_column("Status", style="cyan", no_wrap=True)
206
+
207
+ for agent_id, agent_data in sorted(installed_agents, key=lambda x: x[1]["name"]):
208
+ is_enabled = config_agents.get(agent_id, {}).get("enabled", False)
209
+ status = "[green]✓ Enabled[/green]" if is_enabled else "[dim]Not configured[/dim]"
210
+
211
+ table.add_row(
212
+ agent_data["name"],
213
+ agent_data["short_name"],
214
+ agent_data["description"][:50] + "..."
215
+ if len(agent_data["description"]) > 50
216
+ else agent_data["description"],
217
+ agent_data["author_name"],
218
+ status,
219
+ )
220
+
221
+ _console.print(table)
222
+ _console.print()
223
+
224
+ # Show not installed agents with installation commands
225
+ if not_installed_agents:
226
+ _console.print("[bold yellow]○ Not Installed Agents[/bold yellow]")
227
+ _console.print()
228
+
229
+ table = Table(show_header=True, header_style="bold yellow")
230
+ table.add_column("Name", style="cyan", no_wrap=True)
231
+ table.add_column("Short Name", style="green")
232
+ table.add_column("Description", style="white", max_width=40)
233
+ table.add_column("Install Command", style="magenta", no_wrap=False)
234
+
235
+ for agent_id, agent_data in sorted(not_installed_agents, key=lambda x: x[1]["name"]):
236
+ install_info = get_agent_installation_info(agent_data)
237
+ install_cmd = install_info.get("command", "N/A")
238
+
239
+ # Truncate long commands
240
+ if len(install_cmd) > 40:
241
+ install_cmd = install_cmd[:37] + "..."
242
+
243
+ table.add_row(
244
+ agent_data["name"],
245
+ agent_data["short_name"],
246
+ agent_data["description"][:40] + "..."
247
+ if len(agent_data["description"]) > 40
248
+ else agent_data["description"],
249
+ f"[dim]{install_cmd}[/dim]"
250
+ if install_cmd == "N/A"
251
+ else f"[cyan]{install_cmd}[/cyan]",
252
+ )
253
+
254
+ _console.print(table)
255
+ _console.print()
256
+ _console.print("[dim]💡 To install an agent, run:[/dim]")
257
+ _console.print("[cyan] superqode agents install <short-name>[/cyan]")
258
+ _console.print("[dim] or[/dim]")
259
+ _console.print("[cyan] :acp install <short-name>[/cyan]")
260
+ _console.print()
261
+
262
+ _console.print("[dim]Use 'superqode agents store' to see the beautiful store interface[/dim]")
263
+ _console.print("[dim]Use 'superqode connect acp <short-name>' to connect to an agent[/dim]")
264
+ _console.print()
265
+
266
+
267
+ def format_installation_instructions(agent: "Agent") -> Text:
268
+ """Format installation instructions for an agent.
269
+
270
+ Args:
271
+ agent: Agent dict.
272
+
273
+ Returns:
274
+ Formatted Text object with installation instructions.
275
+ """
276
+ from superqode.agents.registry import get_agent_installation_info
277
+ import asyncio
278
+
279
+ install_info = get_agent_installation_info(agent)
280
+ command = install_info.get("command", "")
281
+ description = install_info.get("description", "Install agent")
282
+ instructions = install_info.get("instructions", "")
283
+
284
+ text = Text()
285
+ text.append(f"\n 📦 ", style="bold cyan")
286
+ text.append(f"Installation Instructions for {agent['name']}\n\n", style="bold")
287
+
288
+ if command:
289
+ text.append(" Installation Command:\n", style="bold yellow")
290
+ text.append(f" {command}\n\n", style="cyan")
291
+
292
+ if instructions:
293
+ text.append(" Instructions:\n", style="bold yellow")
294
+ # Split instructions by lines and format
295
+ for line in instructions.split("\n"):
296
+ if line.strip():
297
+ text.append(f" {line}\n", style="white")
298
+ text.append("\n", style="")
299
+
300
+ # Show requirements if available
301
+ run_command = agent.get("run_command", {}).get("*", "")
302
+ if run_command:
303
+ text.append(" Verification:\n", style="bold yellow")
304
+ text.append(f" After installation, verify with: ", style="dim")
305
+ text.append(f"which {run_command.split()[0]}\n", style="cyan")
306
+ text.append("\n", style="")
307
+
308
+ return text
309
+
310
+
311
+ def show_agent_installation_steps(agent_identifier: str) -> None:
312
+ """Display detailed installation instructions for an agent.
313
+
314
+ Args:
315
+ agent_identifier: Agent short name or identity.
316
+ """
317
+ import asyncio
318
+ from superqode.agents.discovery import (
319
+ get_agent_by_short_name_async,
320
+ get_agent_by_identity_async,
321
+ )
322
+ from superqode.agents.registry import get_all_acp_agents
323
+
324
+ async def show_steps_async():
325
+ # Try to find agent
326
+ agent = await get_agent_by_short_name_async(agent_identifier, include_registry=True)
327
+ if not agent:
328
+ agent = await get_agent_by_identity_async(agent_identifier, include_registry=True)
329
+
330
+ if not agent:
331
+ _console.print(f"[red]Agent '{agent_identifier}' not found.[/red]")
332
+ _console.print("[dim]Use 'superqode agents list' to see available agents.[/dim]")
333
+ return
334
+
335
+ # Check if already installed
336
+ is_installed = check_agent_installed(agent)
337
+ if is_installed:
338
+ _console.print(f"[green]✓ {agent['name']} is already installed![/green]")
339
+ _console.print(
340
+ f"[dim]Run command: {agent.get('run_command', {}).get('*', 'N/A')}[/dim]"
341
+ )
342
+ return
343
+
344
+ # Show installation instructions
345
+ instructions = format_installation_instructions(agent)
346
+ _console.print(instructions)
347
+
348
+ # Show prerequisites
349
+ install_info = get_agent_installation_info(agent)
350
+ command = install_info.get("command", "")
351
+
352
+ if command:
353
+ if "npm" in command:
354
+ _console.print(" Prerequisites:\n", style="bold yellow")
355
+ _console.print(" - Node.js (v16 or higher)\n", style="white")
356
+ _console.print(" - npm (comes with Node.js)\n", style="white")
357
+ elif "pip" in command:
358
+ _console.print(" Prerequisites:\n", style="bold yellow")
359
+ _console.print(" - Python 3.8 or higher\n", style="white")
360
+ _console.print(" - pip (Python package manager)\n", style="white")
361
+ elif "cargo" in command:
362
+ _console.print(" Prerequisites:\n", style="bold yellow")
363
+ _console.print(" - Rust toolchain (rustc and cargo)\n", style="white")
364
+ _console.print(" - Cargo package manager\n", style="white")
365
+
366
+ _console.print("\n Quick Install:\n", style="bold green")
367
+ _console.print(f" {command}\n", style="cyan")
368
+ _console.print("\n Or use SuperQode:\n", style="bold green")
369
+ _console.print(f" superqode agents install {agent['short_name']}\n", style="cyan")
370
+
371
+ asyncio.run(show_steps_async())
372
+
373
+
374
+ def show_agent_details(agent: "Agent") -> None:
375
+ """Show detailed information about a specific agent."""
376
+ _console.print()
377
+ _console.print(f"[bold bright_blue]🤖 {agent['name']}[/bold bright_blue]")
378
+ _console.print(f"[dim]{agent['url']}[/dim]")
379
+ _console.print()
380
+
381
+ # Basic info
382
+ _console.print("[bold cyan]Basic Information:[/bold cyan]")
383
+ _console.print(f" Author: {agent['author_name']} ({agent['author_url']})")
384
+ _console.print(f" Type: {agent['type']}")
385
+ _console.print(f" Protocol: {agent['protocol']}")
386
+ _console.print()
387
+
388
+ # Description
389
+ _console.print("[bold cyan]Description:[/bold cyan]")
390
+ _console.print(f" {agent['description']}")
391
+ _console.print()
392
+
393
+ # Help
394
+ if agent.get("help"):
395
+ _console.print("[bold cyan]Help:[/bold cyan]")
396
+ _console.print(agent["help"])
397
+ _console.print()
398
+
399
+ # Actions
400
+ if agent.get("actions", {}).get("*"):
401
+ _console.print("[bold cyan]Available Actions:[/bold cyan]")
402
+ for action_name, action_data in agent["actions"]["*"].items():
403
+ _console.print(f" [green]{action_name}[/green]: {action_data['description']}")
404
+ _console.print()
405
+
406
+
407
+ def validate_agent_environment(agent: "Agent") -> list[str]:
408
+ """Validate environment variables required for an agent.
409
+
410
+ Args:
411
+ agent: Agent configuration
412
+
413
+ Returns:
414
+ List of missing environment variables
415
+ """
416
+ missing_vars = []
417
+
418
+ # Agent-specific environment variable requirements
419
+ # This could be extended to read from agent config
420
+ agent_name = agent.get("short_name", "").lower()
421
+
422
+ # Define known environment variable requirements for agents
423
+ env_requirements = {
424
+ "claude": ["ANTHROPIC_API_KEY"],
425
+ "opencode": ["ZHIPUAI_API_KEY"], # GLM-4.7 uses ZHIPUAI
426
+ "gemini": ["GOOGLE_API_KEY"],
427
+ "openai": ["OPENAI_API_KEY"],
428
+ "kimi": ["MOONSHOT_API_KEY"],
429
+ "grok": ["XAI_API_KEY"],
430
+ }
431
+
432
+ # Check if agent requires specific environment variables
433
+ if agent_name in env_requirements:
434
+ required_vars = env_requirements[agent_name]
435
+ for var in required_vars:
436
+ if not os.getenv(var):
437
+ missing_vars.append(var)
438
+
439
+ return missing_vars
440
+
441
+
442
+ def perform_agent_health_check(agent: "Agent") -> tuple[bool, list[str]]:
443
+ """Perform comprehensive health checks before launching an agent.
444
+
445
+ Args:
446
+ agent: Agent configuration
447
+
448
+ Returns:
449
+ Tuple of (is_healthy, list_of_issues)
450
+ """
451
+ issues = []
452
+
453
+ # Check 1: Installation status
454
+ if not check_agent_installed(agent):
455
+ issues.append(f"Agent '{agent['short_name']}' is not installed")
456
+ return False, issues
457
+
458
+ # Check 2: Environment variables
459
+ missing_env_vars = validate_agent_environment(agent)
460
+ if missing_env_vars:
461
+ issues.extend([f"Missing environment variable: {var}" for var in missing_env_vars])
462
+
463
+ # Check 3: Run command validity
464
+ run_command = agent.get("run_command", {}).get("*")
465
+ if not run_command:
466
+ issues.append("No run command configured")
467
+ else:
468
+ import shutil
469
+
470
+ cmd_parts = run_command.split()
471
+ if not shutil.which(cmd_parts[0]):
472
+ issues.append(f"Command '{cmd_parts[0]}' not found in PATH")
473
+
474
+ # Check 4: Protocol support
475
+ protocol = agent.get("protocol")
476
+ if protocol != "acp":
477
+ issues.append(f"Unsupported protocol: {protocol} (only ACP is supported)")
478
+
479
+ # Check 5: Required fields
480
+ required_fields = ["identity", "name", "short_name"]
481
+ for field in required_fields:
482
+ if field not in agent:
483
+ issues.append(f"Missing required field: {field}")
484
+
485
+ is_healthy = len(issues) == 0
486
+ return is_healthy, issues
487
+
488
+
489
+ def diagnose_agent_issues(agent: "Agent", issues: list[str]) -> None:
490
+ """Provide helpful diagnostics and solutions for agent issues.
491
+
492
+ Args:
493
+ agent: Agent configuration
494
+ issues: List of identified issues
495
+ """
496
+ if not issues:
497
+ return
498
+
499
+ _console.print(f"[yellow]🔍 Diagnostics for {agent['name']}:[/yellow]")
500
+
501
+ for issue in issues:
502
+ if "not installed" in issue:
503
+ _console.print(f" [red]• {issue}[/red]")
504
+ _console.print(
505
+ f" [cyan]Solution: superqode agents install {agent['short_name']}[/cyan]"
506
+ )
507
+ elif "Missing environment variable" in issue:
508
+ var_name = issue.split(": ")[1]
509
+ _console.print(f" [red]• {issue}[/red]")
510
+ _console.print(f" [cyan]Solution: export {var_name}=your_api_key_here[/cyan]")
511
+ _console.print(
512
+ f" [dim]Get your API key from: {agent.get('author_url', 'the provider website')}[/dim]"
513
+ )
514
+ elif "not found in PATH" in issue:
515
+ cmd_name = issue.split("'")[1]
516
+ _console.print(f" [red]• {issue}[/red]")
517
+ _console.print(
518
+ f" [cyan]Solution: Install {cmd_name} and ensure it's in your PATH[/cyan]"
519
+ )
520
+ elif "Unsupported protocol" in issue:
521
+ _console.print(f" [red]• {issue}[/red]")
522
+ _console.print(f" [dim]This agent uses an unsupported protocol[/dim]")
523
+ else:
524
+ _console.print(f" [red]• {issue}[/red]")
525
+
526
+ _console.print()
527
+
528
+
529
+ async def connect_to_agent(agent_identifier: str, project_dir: str | None = None) -> int:
530
+ """Connect to an ACP coding agent.
531
+
532
+ Args:
533
+ agent_identifier: Short name or identity of the agent
534
+ project_dir: Project directory to work in
535
+
536
+ Returns:
537
+ Exit code
538
+ """
539
+ # Find the agent
540
+ from superqode.agents.discovery import (
541
+ get_agent_by_short_name_async,
542
+ get_agent_by_identity_async,
543
+ )
544
+
545
+ agent = await get_agent_by_short_name_async(agent_identifier)
546
+ if not agent:
547
+ agent = await get_agent_by_identity_async(agent_identifier)
548
+
549
+ if not agent:
550
+ _console.print(f"[red]Agent '{agent_identifier}' not found.[/red]")
551
+ _console.print("[dim]Use 'superqode agents list' to see available agents.[/dim]")
552
+ return 1
553
+
554
+ # Perform comprehensive health check
555
+ _console.print(f"[cyan]🔍 Checking {agent['name']}...[/cyan]")
556
+ is_healthy, issues = perform_agent_health_check(agent)
557
+
558
+ if not is_healthy:
559
+ _console.print(f"[red]❌ Health check failed for {agent['name']}[/red]")
560
+ diagnose_agent_issues(agent, issues)
561
+ return 1
562
+
563
+ _console.print(f"[green]✓ Health check passed[/green]")
564
+ _console.print(f"[green]Connecting to {agent['name']}...[/green]")
565
+
566
+ # Get the run command
567
+ run_command = agent.get("run_command", {}).get("*")
568
+ if not run_command:
569
+ _console.print(f"[red]No run command configured for agent '{agent['name']}'.[/red]")
570
+ return 1
571
+
572
+ # Check if the command exists
573
+ import shutil
574
+
575
+ cmd_parts = run_command.split()
576
+ if not shutil.which(cmd_parts[0]):
577
+ _console.print(
578
+ f"[red]Command '{cmd_parts[0]}' not found. Please install the agent first.[/red]"
579
+ )
580
+ _console.print(f"[dim]Run: superqode agents install {agent['short_name']}[/dim]")
581
+ return 1
582
+
583
+ # Create agent manager
584
+ manager = ACPAgentManager()
585
+
586
+ try:
587
+ # Connect to the agent
588
+ cwd = project_dir or os.getcwd()
589
+ success = await manager.connect_to_agent(run_command, cwd)
590
+
591
+ if not success:
592
+ _console.print(f"[red]Failed to connect to {agent['name']}.[/red]")
593
+ return 1
594
+
595
+ _console.print(f"[green]✓ Connected to {agent['name']}![/green]")
596
+ _console.print(
597
+ "[dim]Type your messages and press Enter. Type 'exit' or 'quit' to disconnect.[/dim]"
598
+ )
599
+ _console.print()
600
+
601
+ # Interactive loop
602
+ while True:
603
+ try:
604
+ # Get user input
605
+ user_input = await asyncio.get_event_loop().run_in_executor(
606
+ None, lambda: input("> ").strip()
607
+ )
608
+
609
+ if user_input.lower() in ("exit", "quit", "q"):
610
+ break
611
+
612
+ if not user_input:
613
+ continue
614
+
615
+ # Send message to agent
616
+ await manager.send_message(user_input)
617
+
618
+ # Receive and display responses
619
+ messages = await manager.receive_messages()
620
+ for message in messages:
621
+ if hasattr(message, "content"):
622
+ print(f"Agent: {message.content}")
623
+ else:
624
+ print(f"Agent: {message}")
625
+
626
+ except KeyboardInterrupt:
627
+ break
628
+ except EOFError:
629
+ break
630
+
631
+ except Exception as e:
632
+ _console.print(f"[red]Connection error: {e}[/red]")
633
+ return 1
634
+ finally:
635
+ await manager.disconnect()
636
+
637
+ _console.print("[green]Disconnected from agent.[/green]")
638
+ return 0
639
+
640
+
641
+ def check_system_dependencies() -> dict[str, bool]:
642
+ """Check for common system dependencies required by agents.
643
+
644
+ Returns:
645
+ Dict mapping dependency names to availability status
646
+ """
647
+ import shutil
648
+
649
+ dependencies = {
650
+ "npm": shutil.which("npm") is not None,
651
+ "node": shutil.which("node") is not None,
652
+ "python": shutil.which("python") is not None or shutil.which("python3") is not None,
653
+ "uv": shutil.which("uv") is not None,
654
+ "pip": shutil.which("pip") is not None or shutil.which("pip3") is not None,
655
+ "curl": shutil.which("curl") is not None,
656
+ }
657
+
658
+ return dependencies
659
+
660
+
661
+ def get_os_command(actions: dict, action_name: str) -> str | None:
662
+ """Get the appropriate command for the current OS."""
663
+ import platform
664
+
665
+ system = platform.system().lower()
666
+
667
+ # Try OS-specific command first
668
+ os_command = actions.get(system, {}).get("command")
669
+ if os_command:
670
+ return os_command
671
+
672
+ # Fall back to wildcard
673
+ wildcard_command = actions.get("*", {}).get("command")
674
+ if wildcard_command:
675
+ return wildcard_command
676
+
677
+ return None
678
+
679
+
680
+ def install_system_dependency(dep_name: str) -> bool:
681
+ """Attempt to install a system dependency.
682
+
683
+ Args:
684
+ dep_name: Name of the dependency to install
685
+
686
+ Returns:
687
+ True if installation succeeded or was skipped
688
+ """
689
+ import subprocess
690
+ import platform
691
+
692
+ system = platform.system().lower()
693
+
694
+ install_commands = {
695
+ "uv": {
696
+ "darwin": "curl -LsSf https://astral.sh/uv/install.sh | sh",
697
+ "linux": "curl -LsSf https://astral.sh/uv/install.sh | sh",
698
+ "windows": 'powershell -c "irm https://astral.sh/uv/install.sh | iex"',
699
+ },
700
+ "npm": {
701
+ "darwin": "brew install node",
702
+ "linux": "curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - && sudo apt-get install -y nodejs",
703
+ "windows": "choco install nodejs",
704
+ },
705
+ }
706
+
707
+ if dep_name in install_commands and system in install_commands[dep_name]:
708
+ cmd = install_commands[dep_name][system]
709
+ _console.print(f"[cyan]Installing {dep_name}...[/cyan]")
710
+ _console.print(f"[dim]{cmd}[/dim]")
711
+
712
+ try:
713
+ result = subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True)
714
+ _console.print(f"[green]✓ {dep_name} installed successfully[/green]")
715
+ return True
716
+ except subprocess.CalledProcessError as e:
717
+ _console.print(f"[yellow]⚠️ Could not auto-install {dep_name}[/yellow]")
718
+ _console.print(f"[dim]Please install {dep_name} manually[/dim]")
719
+ return False
720
+
721
+ return False
722
+
723
+
724
+ def install_agent(agent_identifier: str) -> int:
725
+ """Install an ACP agent with smart dependency detection.
726
+
727
+ Args:
728
+ agent_identifier: Short name or identity of the agent
729
+
730
+ Returns:
731
+ Exit code
732
+ """
733
+ import asyncio
734
+ import subprocess
735
+
736
+ async def install_agent_async():
737
+ # Find the agent (include registry)
738
+ from superqode.agents.discovery import (
739
+ get_agent_by_short_name_async,
740
+ get_agent_by_identity_async,
741
+ )
742
+
743
+ agent = await get_agent_by_short_name_async(agent_identifier, include_registry=True)
744
+ if not agent:
745
+ agent = await get_agent_by_identity_async(agent_identifier, include_registry=True)
746
+
747
+ if not agent:
748
+ _console.print(f"[red]Agent '{agent_identifier}' not found.[/red]")
749
+ _console.print("[dim]Use 'superqode agents list' to see available agents.[/dim]")
750
+ return 1
751
+
752
+ # Get available actions
753
+ actions = agent.get("actions", {})
754
+
755
+ # If no actions in agent, try to get from registry
756
+ if not actions:
757
+ from superqode.agents.registry import get_agent_installation_info
758
+
759
+ install_info = get_agent_installation_info(agent)
760
+ command = install_info.get("command", "")
761
+
762
+ if command:
763
+ # Create temporary actions structure
764
+ actions = {
765
+ "*": {
766
+ "install": {
767
+ "command": command,
768
+ "description": install_info.get(
769
+ "description", f"Install {agent['name']}"
770
+ ),
771
+ }
772
+ }
773
+ }
774
+ else:
775
+ _console.print(
776
+ f"[red]No installation actions available for '{agent['name']}'.[/red]"
777
+ )
778
+ _console.print(
779
+ "[dim]Please check the agent's documentation for installation instructions.[/dim]"
780
+ )
781
+ return 1
782
+
783
+ # Pre-flight checks
784
+ _console.print(f"[bold cyan]🚀 Installing {agent['name']}[/bold cyan]")
785
+ _console.print(f"[dim]{agent.get('description', '')}[/dim]")
786
+ _console.print()
787
+
788
+ # Check system dependencies
789
+ deps = check_system_dependencies()
790
+ missing_deps = [dep for dep, available in deps.items() if not available]
791
+
792
+ if missing_deps:
793
+ _console.print("[yellow]⚠️ Missing system dependencies detected:[/yellow]")
794
+ for dep in missing_deps:
795
+ _console.print(f" [red]• {dep}[/red]")
796
+
797
+ # Try to auto-install critical dependencies
798
+ critical_deps = ["uv"] # Add more as needed
799
+ for dep in critical_deps:
800
+ if dep in missing_deps:
801
+ if not install_system_dependency(dep):
802
+ _console.print(
803
+ f"[red]Cannot proceed without {dep}. Please install it manually.[/red]"
804
+ )
805
+ return 1
806
+
807
+ _console.print()
808
+
809
+ # Get the install command for current OS
810
+ install_command = get_os_command(actions, "install")
811
+ if not install_command:
812
+ _console.print("[red]No installation command found for this agent on your OS.[/red]")
813
+ return 1
814
+
815
+ # Check for bootstrap_uv flag
816
+ needs_uv_bootstrap = False
817
+ for os_actions in actions.values():
818
+ if isinstance(os_actions, dict) and os_actions.get("bootstrap_uv", False):
819
+ needs_uv_bootstrap = True
820
+ break
821
+
822
+ # Bootstrap UV if needed
823
+ if needs_uv_bootstrap and not deps.get("uv", False):
824
+ _console.print("[cyan]Bootstrapping UV package manager...[/cyan]")
825
+ if not install_system_dependency("uv"):
826
+ _console.print("[red]UV bootstrap failed. Cannot proceed.[/red]")
827
+ return 1
828
+
829
+ _console.print(f"[green]Installing {agent['name']}...[/green]")
830
+ _console.print(f"[dim]Command: {install_command}[/dim]")
831
+ _console.print()
832
+
833
+ # Run the installation command with progress feedback
834
+ try:
835
+ # Use a more interactive approach for long-running installs
836
+ process = subprocess.Popen(
837
+ install_command,
838
+ shell=True,
839
+ stdout=subprocess.PIPE,
840
+ stderr=subprocess.PIPE,
841
+ text=True,
842
+ bufsize=1,
843
+ universal_newlines=True,
844
+ )
845
+
846
+ # Show progress
847
+ _console.print("[cyan]Installation in progress...[/cyan]")
848
+
849
+ # Wait for completion
850
+ stdout, stderr = process.communicate()
851
+
852
+ if process.returncode == 0:
853
+ _console.print("[green]✓ Installation completed successfully![/green]")
854
+
855
+ # Verify installation
856
+ if check_agent_installed(agent):
857
+ _console.print(f"[green]✓ Agent '{agent['short_name']}' is ready to use![/green]")
858
+ _console.print(f"[dim]Try: superqode agents connect {agent['short_name']}[/dim]")
859
+ else:
860
+ _console.print("[yellow]⚠️ Agent installed but verification failed[/yellow]")
861
+ _console.print(
862
+ f"[dim]You may need to restart your shell or check the installation manually[/dim]"
863
+ )
864
+
865
+ return 0
866
+ else:
867
+ _console.print(f"[red]✗ Installation failed (exit code {process.returncode})[/red]")
868
+
869
+ # Enhanced error analysis
870
+ error_msg = stderr.lower() if stderr else ""
871
+
872
+ if "eacces" in error_msg or "permission denied" in error_msg:
873
+ _console.print("[yellow]💡 Permission Error:[/yellow]")
874
+ _console.print(" This command requires administrator privileges.")
875
+ _console.print(f" Try: [bold]sudo {install_command}[/bold]")
876
+ _console.print(
877
+ " Or use a Node version manager like nvm/fnm for user-space installation"
878
+ )
879
+ elif "command not found" in error_msg or "npm: command not found" in error_msg:
880
+ _console.print("[yellow]💡 Missing npm:[/yellow]")
881
+ _console.print(" Node.js/npm is not installed. Install from:")
882
+ _console.print(" https://nodejs.org/ or use your system package manager")
883
+ elif "uv: command not found" in error_msg:
884
+ _console.print("[yellow]💡 Missing UV:[/yellow]")
885
+ _console.print(" UV package manager is not installed. Install with:")
886
+ _console.print(" curl -LsSf https://astral.sh/uv/install.sh | sh")
887
+ elif "certificate" in error_msg or "ssl" in error_msg:
888
+ _console.print("[yellow]💡 SSL/Certificate Error:[/yellow]")
889
+ _console.print(" There may be network or certificate issues.")
890
+ _console.print(" Try updating your system's CA certificates.")
891
+ else:
892
+ # Show the actual error output
893
+ if stdout.strip():
894
+ _console.print("[dim]Output:[/dim]")
895
+ _console.print(stdout.strip())
896
+ if stderr.strip():
897
+ _console.print("[dim]Error:[/dim]")
898
+ _console.print(stderr.strip())
899
+
900
+ _console.print(
901
+ f"\n[dim]💡 Alternative: Try installing manually with: {install_command}[/dim]"
902
+ )
903
+ return 1
904
+
905
+ except FileNotFoundError:
906
+ _console.print(
907
+ f"[red]Command not found. Please ensure required dependencies are installed.[/red]"
908
+ )
909
+ return 1
910
+ except Exception as e:
911
+ _console.print(f"[red]Installation error: {e}[/red]")
912
+ return 1
913
+
914
+ try:
915
+ return asyncio.run(install_agent_async())
916
+ except RuntimeError as e:
917
+ if "asyncio.run() cannot be called from a running event loop" in str(e):
918
+ # We're already in an async context
919
+ import nest_asyncio
920
+
921
+ nest_asyncio.apply()
922
+ return asyncio.run(install_agent_async())
923
+ else:
924
+ raise
925
+
926
+
927
+ # Main command functions for CLI integration
928
+ def list_agents() -> None:
929
+ """List all available ACP agents."""
930
+ show_agents_list()
931
+
932
+
933
+ def show_agent(agent_identifier: str) -> None:
934
+ """Show details about a specific agent."""
935
+ import asyncio
936
+
937
+ agent = get_agent_by_short_name(agent_identifier)
938
+ if not agent:
939
+ agent = get_agent_by_identity(agent_identifier)
940
+
941
+ if not agent:
942
+ _console.print(f"[red]Agent '{agent_identifier}' not found.[/red]")
943
+ return
944
+
945
+ show_agent_details(agent)
946
+
947
+
948
+ def connect_agent(agent_identifier: str, project_dir: str | None = None) -> int:
949
+ """Connect to an ACP agent (synchronous wrapper)."""
950
+ try:
951
+ return asyncio.run(connect_to_agent(agent_identifier, project_dir))
952
+ except RuntimeError as e:
953
+ if "asyncio.run() cannot be called from a running event loop" in str(e):
954
+ # We're already in an async context, create a new task
955
+ import nest_asyncio
956
+
957
+ nest_asyncio.apply()
958
+ return asyncio.run(connect_to_agent(agent_identifier, project_dir))
959
+ else:
960
+ raise
961
+
962
+
963
+ def install_agent_cmd(agent_identifier: str) -> int:
964
+ """Install an ACP agent (synchronous wrapper)."""
965
+ return install_agent(agent_identifier)