codepp 0.0.437__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. code_puppy/__init__.py +10 -0
  2. code_puppy/__main__.py +10 -0
  3. code_puppy/agents/__init__.py +31 -0
  4. code_puppy/agents/agent_c_reviewer.py +155 -0
  5. code_puppy/agents/agent_code_puppy.py +117 -0
  6. code_puppy/agents/agent_code_reviewer.py +90 -0
  7. code_puppy/agents/agent_cpp_reviewer.py +132 -0
  8. code_puppy/agents/agent_creator_agent.py +638 -0
  9. code_puppy/agents/agent_golang_reviewer.py +151 -0
  10. code_puppy/agents/agent_helios.py +124 -0
  11. code_puppy/agents/agent_javascript_reviewer.py +160 -0
  12. code_puppy/agents/agent_manager.py +742 -0
  13. code_puppy/agents/agent_pack_leader.py +385 -0
  14. code_puppy/agents/agent_planning.py +165 -0
  15. code_puppy/agents/agent_python_programmer.py +169 -0
  16. code_puppy/agents/agent_python_reviewer.py +90 -0
  17. code_puppy/agents/agent_qa_expert.py +163 -0
  18. code_puppy/agents/agent_qa_kitten.py +208 -0
  19. code_puppy/agents/agent_scheduler.py +121 -0
  20. code_puppy/agents/agent_security_auditor.py +181 -0
  21. code_puppy/agents/agent_terminal_qa.py +323 -0
  22. code_puppy/agents/agent_typescript_reviewer.py +166 -0
  23. code_puppy/agents/base_agent.py +2156 -0
  24. code_puppy/agents/event_stream_handler.py +348 -0
  25. code_puppy/agents/json_agent.py +202 -0
  26. code_puppy/agents/pack/__init__.py +34 -0
  27. code_puppy/agents/pack/bloodhound.py +304 -0
  28. code_puppy/agents/pack/husky.py +327 -0
  29. code_puppy/agents/pack/retriever.py +393 -0
  30. code_puppy/agents/pack/shepherd.py +348 -0
  31. code_puppy/agents/pack/terrier.py +287 -0
  32. code_puppy/agents/pack/watchdog.py +367 -0
  33. code_puppy/agents/prompt_reviewer.py +145 -0
  34. code_puppy/agents/subagent_stream_handler.py +276 -0
  35. code_puppy/api/__init__.py +13 -0
  36. code_puppy/api/app.py +169 -0
  37. code_puppy/api/main.py +21 -0
  38. code_puppy/api/pty_manager.py +453 -0
  39. code_puppy/api/routers/__init__.py +12 -0
  40. code_puppy/api/routers/agents.py +36 -0
  41. code_puppy/api/routers/commands.py +217 -0
  42. code_puppy/api/routers/config.py +75 -0
  43. code_puppy/api/routers/sessions.py +234 -0
  44. code_puppy/api/templates/terminal.html +361 -0
  45. code_puppy/api/websocket.py +154 -0
  46. code_puppy/callbacks.py +692 -0
  47. code_puppy/chatgpt_codex_client.py +338 -0
  48. code_puppy/claude_cache_client.py +672 -0
  49. code_puppy/cli_runner.py +1073 -0
  50. code_puppy/command_line/__init__.py +1 -0
  51. code_puppy/command_line/add_model_menu.py +1092 -0
  52. code_puppy/command_line/agent_menu.py +662 -0
  53. code_puppy/command_line/attachments.py +395 -0
  54. code_puppy/command_line/autosave_menu.py +704 -0
  55. code_puppy/command_line/clipboard.py +527 -0
  56. code_puppy/command_line/colors_menu.py +532 -0
  57. code_puppy/command_line/command_handler.py +293 -0
  58. code_puppy/command_line/command_registry.py +150 -0
  59. code_puppy/command_line/config_commands.py +719 -0
  60. code_puppy/command_line/core_commands.py +867 -0
  61. code_puppy/command_line/diff_menu.py +865 -0
  62. code_puppy/command_line/file_path_completion.py +73 -0
  63. code_puppy/command_line/load_context_completion.py +52 -0
  64. code_puppy/command_line/mcp/__init__.py +10 -0
  65. code_puppy/command_line/mcp/base.py +32 -0
  66. code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
  67. code_puppy/command_line/mcp/custom_server_form.py +688 -0
  68. code_puppy/command_line/mcp/custom_server_installer.py +195 -0
  69. code_puppy/command_line/mcp/edit_command.py +148 -0
  70. code_puppy/command_line/mcp/handler.py +138 -0
  71. code_puppy/command_line/mcp/help_command.py +147 -0
  72. code_puppy/command_line/mcp/install_command.py +214 -0
  73. code_puppy/command_line/mcp/install_menu.py +705 -0
  74. code_puppy/command_line/mcp/list_command.py +94 -0
  75. code_puppy/command_line/mcp/logs_command.py +235 -0
  76. code_puppy/command_line/mcp/remove_command.py +82 -0
  77. code_puppy/command_line/mcp/restart_command.py +100 -0
  78. code_puppy/command_line/mcp/search_command.py +123 -0
  79. code_puppy/command_line/mcp/start_all_command.py +135 -0
  80. code_puppy/command_line/mcp/start_command.py +117 -0
  81. code_puppy/command_line/mcp/status_command.py +184 -0
  82. code_puppy/command_line/mcp/stop_all_command.py +112 -0
  83. code_puppy/command_line/mcp/stop_command.py +80 -0
  84. code_puppy/command_line/mcp/test_command.py +107 -0
  85. code_puppy/command_line/mcp/utils.py +129 -0
  86. code_puppy/command_line/mcp/wizard_utils.py +334 -0
  87. code_puppy/command_line/mcp_completion.py +174 -0
  88. code_puppy/command_line/model_picker_completion.py +197 -0
  89. code_puppy/command_line/model_settings_menu.py +932 -0
  90. code_puppy/command_line/motd.py +96 -0
  91. code_puppy/command_line/onboarding_slides.py +179 -0
  92. code_puppy/command_line/onboarding_wizard.py +342 -0
  93. code_puppy/command_line/pin_command_completion.py +329 -0
  94. code_puppy/command_line/prompt_toolkit_completion.py +846 -0
  95. code_puppy/command_line/session_commands.py +302 -0
  96. code_puppy/command_line/shell_passthrough.py +145 -0
  97. code_puppy/command_line/skills_completion.py +160 -0
  98. code_puppy/command_line/uc_menu.py +893 -0
  99. code_puppy/command_line/utils.py +93 -0
  100. code_puppy/command_line/wiggum_state.py +78 -0
  101. code_puppy/config.py +1770 -0
  102. code_puppy/error_logging.py +134 -0
  103. code_puppy/gemini_code_assist.py +385 -0
  104. code_puppy/gemini_model.py +754 -0
  105. code_puppy/hook_engine/README.md +105 -0
  106. code_puppy/hook_engine/__init__.py +21 -0
  107. code_puppy/hook_engine/aliases.py +155 -0
  108. code_puppy/hook_engine/engine.py +221 -0
  109. code_puppy/hook_engine/executor.py +296 -0
  110. code_puppy/hook_engine/matcher.py +156 -0
  111. code_puppy/hook_engine/models.py +240 -0
  112. code_puppy/hook_engine/registry.py +106 -0
  113. code_puppy/hook_engine/validator.py +144 -0
  114. code_puppy/http_utils.py +361 -0
  115. code_puppy/keymap.py +128 -0
  116. code_puppy/main.py +10 -0
  117. code_puppy/mcp_/__init__.py +66 -0
  118. code_puppy/mcp_/async_lifecycle.py +286 -0
  119. code_puppy/mcp_/blocking_startup.py +469 -0
  120. code_puppy/mcp_/captured_stdio_server.py +275 -0
  121. code_puppy/mcp_/circuit_breaker.py +290 -0
  122. code_puppy/mcp_/config_wizard.py +507 -0
  123. code_puppy/mcp_/dashboard.py +308 -0
  124. code_puppy/mcp_/error_isolation.py +407 -0
  125. code_puppy/mcp_/examples/retry_example.py +226 -0
  126. code_puppy/mcp_/health_monitor.py +589 -0
  127. code_puppy/mcp_/managed_server.py +428 -0
  128. code_puppy/mcp_/manager.py +807 -0
  129. code_puppy/mcp_/mcp_logs.py +224 -0
  130. code_puppy/mcp_/registry.py +451 -0
  131. code_puppy/mcp_/retry_manager.py +337 -0
  132. code_puppy/mcp_/server_registry_catalog.py +1126 -0
  133. code_puppy/mcp_/status_tracker.py +355 -0
  134. code_puppy/mcp_/system_tools.py +209 -0
  135. code_puppy/mcp_prompts/__init__.py +1 -0
  136. code_puppy/mcp_prompts/hook_creator.py +103 -0
  137. code_puppy/messaging/__init__.py +255 -0
  138. code_puppy/messaging/bus.py +613 -0
  139. code_puppy/messaging/commands.py +167 -0
  140. code_puppy/messaging/markdown_patches.py +57 -0
  141. code_puppy/messaging/message_queue.py +361 -0
  142. code_puppy/messaging/messages.py +569 -0
  143. code_puppy/messaging/queue_console.py +271 -0
  144. code_puppy/messaging/renderers.py +311 -0
  145. code_puppy/messaging/rich_renderer.py +1158 -0
  146. code_puppy/messaging/spinner/__init__.py +83 -0
  147. code_puppy/messaging/spinner/console_spinner.py +240 -0
  148. code_puppy/messaging/spinner/spinner_base.py +95 -0
  149. code_puppy/messaging/subagent_console.py +460 -0
  150. code_puppy/model_factory.py +848 -0
  151. code_puppy/model_switching.py +63 -0
  152. code_puppy/model_utils.py +168 -0
  153. code_puppy/models.json +174 -0
  154. code_puppy/models_dev_api.json +1 -0
  155. code_puppy/models_dev_parser.py +592 -0
  156. code_puppy/plugins/__init__.py +186 -0
  157. code_puppy/plugins/agent_skills/__init__.py +22 -0
  158. code_puppy/plugins/agent_skills/config.py +175 -0
  159. code_puppy/plugins/agent_skills/discovery.py +136 -0
  160. code_puppy/plugins/agent_skills/downloader.py +392 -0
  161. code_puppy/plugins/agent_skills/installer.py +22 -0
  162. code_puppy/plugins/agent_skills/metadata.py +219 -0
  163. code_puppy/plugins/agent_skills/prompt_builder.py +60 -0
  164. code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
  165. code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
  166. code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
  167. code_puppy/plugins/agent_skills/skills_install_menu.py +664 -0
  168. code_puppy/plugins/agent_skills/skills_menu.py +781 -0
  169. code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
  170. code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
  171. code_puppy/plugins/antigravity_oauth/antigravity_model.py +706 -0
  172. code_puppy/plugins/antigravity_oauth/config.py +42 -0
  173. code_puppy/plugins/antigravity_oauth/constants.py +133 -0
  174. code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
  175. code_puppy/plugins/antigravity_oauth/register_callbacks.py +518 -0
  176. code_puppy/plugins/antigravity_oauth/storage.py +288 -0
  177. code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
  178. code_puppy/plugins/antigravity_oauth/token.py +167 -0
  179. code_puppy/plugins/antigravity_oauth/transport.py +863 -0
  180. code_puppy/plugins/antigravity_oauth/utils.py +168 -0
  181. code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
  182. code_puppy/plugins/chatgpt_oauth/config.py +52 -0
  183. code_puppy/plugins/chatgpt_oauth/oauth_flow.py +329 -0
  184. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
  185. code_puppy/plugins/chatgpt_oauth/test_plugin.py +301 -0
  186. code_puppy/plugins/chatgpt_oauth/utils.py +523 -0
  187. code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
  188. code_puppy/plugins/claude_code_hooks/config.py +137 -0
  189. code_puppy/plugins/claude_code_hooks/register_callbacks.py +175 -0
  190. code_puppy/plugins/claude_code_oauth/README.md +167 -0
  191. code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
  192. code_puppy/plugins/claude_code_oauth/__init__.py +25 -0
  193. code_puppy/plugins/claude_code_oauth/config.py +52 -0
  194. code_puppy/plugins/claude_code_oauth/register_callbacks.py +453 -0
  195. code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
  196. code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
  197. code_puppy/plugins/claude_code_oauth/utils.py +640 -0
  198. code_puppy/plugins/customizable_commands/__init__.py +0 -0
  199. code_puppy/plugins/customizable_commands/register_callbacks.py +152 -0
  200. code_puppy/plugins/example_custom_command/README.md +280 -0
  201. code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
  202. code_puppy/plugins/file_permission_handler/__init__.py +4 -0
  203. code_puppy/plugins/file_permission_handler/register_callbacks.py +470 -0
  204. code_puppy/plugins/frontend_emitter/__init__.py +25 -0
  205. code_puppy/plugins/frontend_emitter/emitter.py +121 -0
  206. code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
  207. code_puppy/plugins/hook_creator/__init__.py +1 -0
  208. code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
  209. code_puppy/plugins/hook_manager/__init__.py +1 -0
  210. code_puppy/plugins/hook_manager/config.py +290 -0
  211. code_puppy/plugins/hook_manager/hooks_menu.py +564 -0
  212. code_puppy/plugins/hook_manager/register_callbacks.py +227 -0
  213. code_puppy/plugins/oauth_puppy_html.py +228 -0
  214. code_puppy/plugins/scheduler/__init__.py +1 -0
  215. code_puppy/plugins/scheduler/register_callbacks.py +88 -0
  216. code_puppy/plugins/scheduler/scheduler_menu.py +522 -0
  217. code_puppy/plugins/scheduler/scheduler_wizard.py +341 -0
  218. code_puppy/plugins/shell_safety/__init__.py +6 -0
  219. code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
  220. code_puppy/plugins/shell_safety/command_cache.py +156 -0
  221. code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
  222. code_puppy/plugins/synthetic_status/__init__.py +1 -0
  223. code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
  224. code_puppy/plugins/synthetic_status/status_api.py +147 -0
  225. code_puppy/plugins/universal_constructor/__init__.py +13 -0
  226. code_puppy/plugins/universal_constructor/models.py +138 -0
  227. code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
  228. code_puppy/plugins/universal_constructor/registry.py +302 -0
  229. code_puppy/plugins/universal_constructor/sandbox.py +584 -0
  230. code_puppy/prompts/antigravity_system_prompt.md +1 -0
  231. code_puppy/pydantic_patches.py +356 -0
  232. code_puppy/reopenable_async_client.py +232 -0
  233. code_puppy/round_robin_model.py +150 -0
  234. code_puppy/scheduler/__init__.py +41 -0
  235. code_puppy/scheduler/__main__.py +9 -0
  236. code_puppy/scheduler/cli.py +118 -0
  237. code_puppy/scheduler/config.py +126 -0
  238. code_puppy/scheduler/daemon.py +280 -0
  239. code_puppy/scheduler/executor.py +155 -0
  240. code_puppy/scheduler/platform.py +19 -0
  241. code_puppy/scheduler/platform_unix.py +22 -0
  242. code_puppy/scheduler/platform_win.py +32 -0
  243. code_puppy/session_storage.py +338 -0
  244. code_puppy/status_display.py +257 -0
  245. code_puppy/summarization_agent.py +176 -0
  246. code_puppy/terminal_utils.py +418 -0
  247. code_puppy/tools/__init__.py +501 -0
  248. code_puppy/tools/agent_tools.py +603 -0
  249. code_puppy/tools/ask_user_question/__init__.py +26 -0
  250. code_puppy/tools/ask_user_question/constants.py +73 -0
  251. code_puppy/tools/ask_user_question/demo_tui.py +55 -0
  252. code_puppy/tools/ask_user_question/handler.py +232 -0
  253. code_puppy/tools/ask_user_question/models.py +304 -0
  254. code_puppy/tools/ask_user_question/registration.py +26 -0
  255. code_puppy/tools/ask_user_question/renderers.py +309 -0
  256. code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
  257. code_puppy/tools/ask_user_question/theme.py +155 -0
  258. code_puppy/tools/ask_user_question/tui_loop.py +423 -0
  259. code_puppy/tools/browser/__init__.py +37 -0
  260. code_puppy/tools/browser/browser_control.py +289 -0
  261. code_puppy/tools/browser/browser_interactions.py +545 -0
  262. code_puppy/tools/browser/browser_locators.py +640 -0
  263. code_puppy/tools/browser/browser_manager.py +378 -0
  264. code_puppy/tools/browser/browser_navigation.py +251 -0
  265. code_puppy/tools/browser/browser_screenshot.py +179 -0
  266. code_puppy/tools/browser/browser_scripts.py +462 -0
  267. code_puppy/tools/browser/browser_workflows.py +221 -0
  268. code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
  269. code_puppy/tools/browser/terminal_command_tools.py +534 -0
  270. code_puppy/tools/browser/terminal_screenshot_tools.py +552 -0
  271. code_puppy/tools/browser/terminal_tools.py +525 -0
  272. code_puppy/tools/command_runner.py +1346 -0
  273. code_puppy/tools/common.py +1409 -0
  274. code_puppy/tools/display.py +84 -0
  275. code_puppy/tools/file_modifications.py +886 -0
  276. code_puppy/tools/file_operations.py +802 -0
  277. code_puppy/tools/scheduler_tools.py +412 -0
  278. code_puppy/tools/skills_tools.py +244 -0
  279. code_puppy/tools/subagent_context.py +158 -0
  280. code_puppy/tools/tools_content.py +51 -0
  281. code_puppy/tools/universal_constructor.py +889 -0
  282. code_puppy/uvx_detection.py +242 -0
  283. code_puppy/version_checker.py +82 -0
  284. codepp-0.0.437.dist-info/METADATA +766 -0
  285. codepp-0.0.437.dist-info/RECORD +288 -0
  286. codepp-0.0.437.dist-info/WHEEL +4 -0
  287. codepp-0.0.437.dist-info/entry_points.txt +3 -0
  288. codepp-0.0.437.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,135 @@
1
+ """
2
+ MCP Start All Command - Starts all registered MCP servers.
3
+ """
4
+
5
+ import logging
6
+ import time
7
+ from typing import List, Optional
8
+
9
+ from rich.text import Text
10
+
11
+ from code_puppy.mcp_.managed_server import ServerState
12
+ from code_puppy.messaging import emit_info
13
+
14
+ from ...agents import get_current_agent
15
+ from .base import MCPCommandBase
16
+
17
+ # Configure logging
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class StartAllCommand(MCPCommandBase):
22
+ """
23
+ Command handler for starting all MCP servers.
24
+
25
+ Starts all registered MCP servers and provides a summary of results.
26
+ """
27
+
28
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
29
+ """
30
+ Start all registered MCP servers.
31
+
32
+ Args:
33
+ args: Command arguments (unused)
34
+ group_id: Optional message group ID for grouping related messages
35
+ """
36
+ if group_id is None:
37
+ group_id = self.generate_group_id()
38
+
39
+ try:
40
+ servers = self.manager.list_servers()
41
+
42
+ if not servers:
43
+ emit_info(
44
+ "[yellow]No servers registered[/yellow]", message_group=group_id
45
+ )
46
+ return
47
+
48
+ started_count = 0
49
+ failed_count = 0
50
+ already_running = 0
51
+
52
+ emit_info(f"Starting {len(servers)} servers...", message_group=group_id)
53
+
54
+ for server_info in servers:
55
+ server_id = server_info.id
56
+ server_name = server_info.name
57
+
58
+ # Skip if already running
59
+ if server_info.state == ServerState.RUNNING:
60
+ already_running += 1
61
+ emit_info(
62
+ f" • {server_name}: already running", message_group=group_id
63
+ )
64
+ continue
65
+
66
+ # Try to start the server
67
+ success = self.manager.start_server_sync(server_id)
68
+
69
+ if success:
70
+ started_count += 1
71
+ emit_info(
72
+ Text.from_markup(f" [green]✓ Started: {server_name}[/green]"),
73
+ message_group=group_id,
74
+ )
75
+ else:
76
+ failed_count += 1
77
+ emit_info(
78
+ Text.from_markup(f" [red]✗ Failed: {server_name}[/red]"),
79
+ message_group=group_id,
80
+ )
81
+
82
+ # Summary
83
+ emit_info("", message_group=group_id)
84
+ if started_count > 0:
85
+ emit_info(
86
+ Text.from_markup(
87
+ f"[green]Started {started_count} server(s)[/green]"
88
+ ),
89
+ message_group=group_id,
90
+ )
91
+ if already_running > 0:
92
+ emit_info(
93
+ f"{already_running} server(s) already running",
94
+ message_group=group_id,
95
+ )
96
+ if failed_count > 0:
97
+ emit_info(
98
+ Text.from_markup(
99
+ f"[yellow]Failed to start {failed_count} server(s)[/yellow]"
100
+ ),
101
+ message_group=group_id,
102
+ )
103
+
104
+ # Reload agent if any servers were started
105
+ if started_count > 0:
106
+ # Give async tasks a moment to complete before reloading agent
107
+ try:
108
+ import asyncio
109
+
110
+ asyncio.get_running_loop() # Check if in async context
111
+ # If we're in async context, wait a bit for servers to start
112
+ time.sleep(0.5) # Small delay to let async tasks progress
113
+ except RuntimeError:
114
+ pass # No async loop, servers will start when agent uses them
115
+
116
+ try:
117
+ agent = get_current_agent()
118
+ agent.reload_code_generation_agent()
119
+ # Update MCP tool cache immediately so token counts reflect the change
120
+ agent.update_mcp_tool_cache_sync()
121
+ emit_info(
122
+ Text.from_markup(
123
+ "[dim]Agent reloaded with updated servers[/dim]"
124
+ ),
125
+ message_group=group_id,
126
+ )
127
+ except Exception as e:
128
+ logger.warning(f"Could not reload agent: {e}")
129
+
130
+ except Exception as e:
131
+ logger.error(f"Error starting all servers: {e}")
132
+ emit_info(
133
+ Text.from_markup(f"[red]Failed to start servers: {e}[/red]"),
134
+ message_group=group_id,
135
+ )
@@ -0,0 +1,117 @@
1
+ """
2
+ MCP Start Command - Starts a specific MCP server.
3
+ """
4
+
5
+ import logging
6
+ from typing import List, Optional
7
+
8
+ from rich.text import Text
9
+
10
+ from code_puppy.messaging import emit_error, emit_info, emit_success
11
+
12
+ from ...agents import get_current_agent
13
+ from .base import MCPCommandBase
14
+ from .utils import find_server_id_by_name, suggest_similar_servers
15
+
16
+ # Configure logging
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class StartCommand(MCPCommandBase):
21
+ """
22
+ Command handler for starting MCP servers.
23
+
24
+ Starts a specific MCP server by name and reloads the agent.
25
+ The server subprocess starts asynchronously in the background.
26
+ """
27
+
28
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
29
+ """
30
+ Start a specific MCP server.
31
+
32
+ Args:
33
+ args: Command arguments, expects [server_name]
34
+ group_id: Optional message group ID for grouping related messages
35
+ """
36
+ if group_id is None:
37
+ group_id = self.generate_group_id()
38
+
39
+ if not args:
40
+ emit_info(
41
+ Text.from_markup("[yellow]Usage: /mcp start <server_name>[/yellow]"),
42
+ message_group=group_id,
43
+ )
44
+ return
45
+
46
+ server_name = args[0]
47
+
48
+ try:
49
+ # Find server by name
50
+ server_id = find_server_id_by_name(self.manager, server_name)
51
+ if not server_id:
52
+ emit_error(
53
+ f"Server '{server_name}' not found",
54
+ message_group=group_id,
55
+ )
56
+ suggest_similar_servers(self.manager, server_name, group_id=group_id)
57
+ return
58
+
59
+ # Get server info for better messaging (safely handle missing method)
60
+ server_type = "unknown"
61
+ try:
62
+ if hasattr(self.manager, "get_server_by_name"):
63
+ server_config = self.manager.get_server_by_name(server_name)
64
+ server_type = (
65
+ getattr(server_config, "type", "unknown")
66
+ if server_config
67
+ else "unknown"
68
+ )
69
+ except Exception:
70
+ pass # Default to unknown type if we can't determine it
71
+
72
+ # Start the server (schedules async start in background)
73
+ success = self.manager.start_server_sync(server_id)
74
+
75
+ if success:
76
+ if server_type == "stdio":
77
+ # Stdio servers start subprocess asynchronously
78
+ emit_success(
79
+ f"🚀 Starting server: {server_name} (subprocess starting in background)",
80
+ message_group=group_id,
81
+ )
82
+ emit_info(
83
+ Text.from_markup(
84
+ "[dim]Tip: Use /mcp status to check if the server is fully initialized[/dim]"
85
+ ),
86
+ message_group=group_id,
87
+ )
88
+ else:
89
+ # SSE/HTTP servers connect on first use
90
+ emit_success(
91
+ f"✅ Enabled server: {server_name}",
92
+ message_group=group_id,
93
+ )
94
+
95
+ # Reload the agent to pick up the newly enabled server
96
+ # NOTE: We don't block or wait - the server will be ready
97
+ # when the next prompt runs (pydantic-ai handles connection)
98
+ try:
99
+ agent = get_current_agent()
100
+ agent.reload_code_generation_agent()
101
+ # Clear MCP tool cache - it will be repopulated on next run
102
+ agent.update_mcp_tool_cache_sync()
103
+ emit_info(
104
+ "Agent reloaded with updated servers",
105
+ message_group=group_id,
106
+ )
107
+ except Exception as e:
108
+ logger.warning(f"Could not reload agent: {e}")
109
+ else:
110
+ emit_error(
111
+ f"Failed to start server: {server_name}",
112
+ message_group=group_id,
113
+ )
114
+
115
+ except Exception as e:
116
+ logger.error(f"Error starting server '{server_name}': {e}")
117
+ emit_error(f"Failed to start server: {e}", message_group=group_id)
@@ -0,0 +1,184 @@
1
+ """
2
+ MCP Status Command - Shows detailed status for MCP servers.
3
+ """
4
+
5
+ import logging
6
+ from datetime import datetime
7
+ from typing import List, Optional
8
+
9
+ from rich.panel import Panel
10
+ from rich.text import Text
11
+
12
+ from code_puppy.mcp_.managed_server import ServerState
13
+ from code_puppy.messaging import emit_error, emit_info
14
+
15
+ from .base import MCPCommandBase
16
+ from .list_command import ListCommand
17
+ from .utils import (
18
+ find_server_id_by_name,
19
+ format_state_indicator,
20
+ format_uptime,
21
+ suggest_similar_servers,
22
+ )
23
+
24
+ # Configure logging
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class StatusCommand(MCPCommandBase):
29
+ """
30
+ Command handler for showing MCP server status.
31
+
32
+ Shows detailed status for a specific server or brief status for all servers.
33
+ """
34
+
35
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
36
+ """
37
+ Show detailed status for a specific server or all servers.
38
+
39
+ Args:
40
+ args: Command arguments, expects [server_name] (optional)
41
+ group_id: Optional message group ID for grouping related messages
42
+ """
43
+ if group_id is None:
44
+ group_id = self.generate_group_id()
45
+
46
+ try:
47
+ if args:
48
+ # Show detailed status for specific server
49
+ server_name = args[0]
50
+ server_id = find_server_id_by_name(self.manager, server_name)
51
+
52
+ if not server_id:
53
+ emit_info(
54
+ f"Server '{server_name}' not found", message_group=group_id
55
+ )
56
+ suggest_similar_servers(
57
+ self.manager, server_name, group_id=group_id
58
+ )
59
+ return
60
+
61
+ self._show_detailed_server_status(server_id, server_name, group_id)
62
+ else:
63
+ # Show brief status for all servers
64
+ list_command = ListCommand()
65
+ list_command.execute([], group_id=group_id)
66
+
67
+ except Exception as e:
68
+ logger.error(f"Error showing server status: {e}")
69
+ emit_info(f"Failed to get server status: {e}", message_group=group_id)
70
+
71
+ def _show_detailed_server_status(
72
+ self, server_id: str, server_name: str, group_id: Optional[str] = None
73
+ ) -> None:
74
+ """
75
+ Show comprehensive status information for a specific server.
76
+
77
+ Args:
78
+ server_id: ID of the server
79
+ server_name: Name of the server
80
+ group_id: Optional message group ID
81
+ """
82
+ if group_id is None:
83
+ group_id = self.generate_group_id()
84
+
85
+ try:
86
+ status = self.manager.get_server_status(server_id)
87
+
88
+ if not status.get("exists", True):
89
+ emit_info(
90
+ f"Server '{server_name}' not found or not accessible",
91
+ message_group=group_id,
92
+ )
93
+ return
94
+
95
+ # Create detailed status panel
96
+ status_lines = []
97
+
98
+ # Basic information
99
+ status_lines.append(f"[bold]Server:[/bold] {server_name}")
100
+ status_lines.append(f"[bold]ID:[/bold] {server_id}")
101
+ status_lines.append(
102
+ f"[bold]Type:[/bold] {status.get('type', 'unknown').upper()}"
103
+ )
104
+
105
+ # State and status
106
+ state = status.get("state", "unknown")
107
+ state_display = format_state_indicator(
108
+ ServerState(state)
109
+ if state in [s.value for s in ServerState]
110
+ else ServerState.STOPPED
111
+ )
112
+ status_lines.append(f"[bold]State:[/bold] {state_display}")
113
+
114
+ enabled = status.get("enabled", False)
115
+ status_lines.append(
116
+ f"[bold]Enabled:[/bold] {'✓ Yes' if enabled else '✗ No'}"
117
+ )
118
+
119
+ # Check async lifecycle manager status if available
120
+ try:
121
+ from code_puppy.mcp_.async_lifecycle import get_lifecycle_manager
122
+
123
+ lifecycle_mgr = get_lifecycle_manager()
124
+ if lifecycle_mgr.is_running(server_id):
125
+ status_lines.append(
126
+ "[bold]Process:[/bold] [green]✓ Active (subprocess/connection running)[/green]"
127
+ )
128
+ else:
129
+ status_lines.append("[bold]Process:[/bold] [dim]Not active[/dim]")
130
+ except Exception:
131
+ pass # Lifecycle manager not available
132
+
133
+ quarantined = status.get("quarantined", False)
134
+ if quarantined:
135
+ status_lines.append("[bold]Quarantined:[/bold] [yellow]⚠ Yes[/yellow]")
136
+
137
+ # Timing information
138
+ uptime = status.get("tracker_uptime")
139
+ if uptime:
140
+ uptime_str = format_uptime(
141
+ uptime.total_seconds()
142
+ if hasattr(uptime, "total_seconds")
143
+ else uptime
144
+ )
145
+ status_lines.append(f"[bold]Uptime:[/bold] {uptime_str}")
146
+
147
+ # Error information
148
+ error_msg = status.get("error_message")
149
+ if error_msg:
150
+ status_lines.append(f"[bold]Error:[/bold] [red]{error_msg}[/red]")
151
+
152
+ # Event information
153
+ event_count = status.get("recent_events_count", 0)
154
+ status_lines.append(f"[bold]Recent Events:[/bold] {event_count}")
155
+
156
+ # Metadata
157
+ metadata = status.get("tracker_metadata", {})
158
+ if metadata:
159
+ status_lines.append(f"[bold]Metadata:[/bold] {len(metadata)} keys")
160
+
161
+ # Create and show the panel
162
+ panel_content = Text.from_markup("\n".join(status_lines))
163
+ panel = Panel(
164
+ panel_content, title=f"🔌 {server_name} Status", border_style="cyan"
165
+ )
166
+
167
+ emit_info(panel, message_group=group_id)
168
+
169
+ # Show recent events if available
170
+ recent_events = status.get("recent_events", [])
171
+ if recent_events:
172
+ emit_info("\n📋 Recent Events:", message_group=group_id)
173
+ for event in recent_events[-5:]: # Show last 5 events
174
+ timestamp = datetime.fromisoformat(event["timestamp"])
175
+ time_str = timestamp.strftime("%H:%M:%S")
176
+ emit_info(
177
+ f" {time_str}: {event['message']}", message_group=group_id
178
+ )
179
+
180
+ except Exception as e:
181
+ logger.error(
182
+ f"Error getting detailed status for server '{server_name}': {e}"
183
+ )
184
+ emit_error(f"Error getting server status: {e}", message_group=group_id)
@@ -0,0 +1,112 @@
1
+ """
2
+ MCP Stop All Command - Stops all running MCP servers.
3
+ """
4
+
5
+ import logging
6
+ import time
7
+ from typing import List, Optional
8
+
9
+ from rich.text import Text
10
+
11
+ from code_puppy.mcp_.managed_server import ServerState
12
+ from code_puppy.messaging import emit_info
13
+
14
+ from ...agents import get_current_agent
15
+ from .base import MCPCommandBase
16
+
17
+ # Configure logging
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class StopAllCommand(MCPCommandBase):
22
+ """
23
+ Command handler for stopping all MCP servers.
24
+
25
+ Stops all running MCP servers and provides a summary of results.
26
+ """
27
+
28
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
29
+ """
30
+ Stop all running MCP servers.
31
+
32
+ Args:
33
+ args: Command arguments (unused)
34
+ group_id: Optional message group ID for grouping related messages
35
+ """
36
+ if group_id is None:
37
+ group_id = self.generate_group_id()
38
+
39
+ try:
40
+ servers = self.manager.list_servers()
41
+
42
+ if not servers:
43
+ emit_info("No servers registered", message_group=group_id)
44
+ return
45
+
46
+ stopped_count = 0
47
+ failed_count = 0
48
+
49
+ # Count running servers
50
+ running_servers = [s for s in servers if s.state == ServerState.RUNNING]
51
+
52
+ if not running_servers:
53
+ emit_info("No servers are currently running", message_group=group_id)
54
+ return
55
+
56
+ emit_info(
57
+ f"Stopping {len(running_servers)} running server(s)...",
58
+ message_group=group_id,
59
+ )
60
+
61
+ for server_info in running_servers:
62
+ server_id = server_info.id
63
+ server_name = server_info.name
64
+
65
+ # Try to stop the server
66
+ success = self.manager.stop_server_sync(server_id)
67
+
68
+ if success:
69
+ stopped_count += 1
70
+ emit_info(f" ✓ Stopped: {server_name}", message_group=group_id)
71
+ else:
72
+ failed_count += 1
73
+ emit_info(f" ✗ Failed: {server_name}", message_group=group_id)
74
+
75
+ # Summary
76
+ emit_info("", message_group=group_id)
77
+ if stopped_count > 0:
78
+ emit_info(f"Stopped {stopped_count} server(s)", message_group=group_id)
79
+ if failed_count > 0:
80
+ emit_info(
81
+ f"Failed to stop {failed_count} server(s)", message_group=group_id
82
+ )
83
+
84
+ # Reload agent if any servers were stopped
85
+ if stopped_count > 0:
86
+ # Give async tasks a moment to complete before reloading agent
87
+ try:
88
+ import asyncio
89
+
90
+ asyncio.get_running_loop() # Check if in async context
91
+ # If we're in async context, wait a bit for servers to stop
92
+ time.sleep(0.5) # Small delay to let async tasks progress
93
+ except RuntimeError:
94
+ pass # No async loop, servers will stop when needed
95
+
96
+ try:
97
+ agent = get_current_agent()
98
+ agent.reload_code_generation_agent()
99
+ # Update MCP tool cache immediately so token counts reflect the change
100
+ agent.update_mcp_tool_cache_sync()
101
+ emit_info(
102
+ Text.from_markup(
103
+ "[dim]Agent reloaded with updated servers[/dim]"
104
+ ),
105
+ message_group=group_id,
106
+ )
107
+ except Exception as e:
108
+ logger.warning(f"Could not reload agent: {e}")
109
+
110
+ except Exception as e:
111
+ logger.error(f"Error stopping all servers: {e}")
112
+ emit_info(f"Failed to stop servers: {e}", message_group=group_id)
@@ -0,0 +1,80 @@
1
+ """
2
+ MCP Stop Command - Stops a specific MCP server.
3
+ """
4
+
5
+ import logging
6
+ from typing import List, Optional
7
+
8
+ from rich.text import Text
9
+
10
+ from code_puppy.messaging import emit_error, emit_info
11
+
12
+ from ...agents import get_current_agent
13
+ from .base import MCPCommandBase
14
+ from .utils import find_server_id_by_name, suggest_similar_servers
15
+
16
+ # Configure logging
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class StopCommand(MCPCommandBase):
21
+ """
22
+ Command handler for stopping MCP servers.
23
+
24
+ Stops a specific MCP server by name and reloads the agent.
25
+ """
26
+
27
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
28
+ """
29
+ Stop a specific MCP server.
30
+
31
+ Args:
32
+ args: Command arguments, expects [server_name]
33
+ group_id: Optional message group ID for grouping related messages
34
+ """
35
+ if group_id is None:
36
+ group_id = self.generate_group_id()
37
+
38
+ if not args:
39
+ emit_info(
40
+ Text.from_markup("[yellow]Usage: /mcp stop <server_name>[/yellow]"),
41
+ message_group=group_id,
42
+ )
43
+ return
44
+
45
+ server_name = args[0]
46
+
47
+ try:
48
+ # Find server by name
49
+ server_id = find_server_id_by_name(self.manager, server_name)
50
+ if not server_id:
51
+ emit_info(f"Server '{server_name}' not found", message_group=group_id)
52
+ suggest_similar_servers(self.manager, server_name, group_id=group_id)
53
+ return
54
+
55
+ # Stop the server (disable and stop process)
56
+ success = self.manager.stop_server_sync(server_id)
57
+
58
+ if success:
59
+ emit_info(f"✓ Stopped server: {server_name}", message_group=group_id)
60
+
61
+ # Reload the agent to remove the disabled server
62
+ try:
63
+ agent = get_current_agent()
64
+ agent.reload_code_generation_agent()
65
+ # Update MCP tool cache immediately so token counts reflect the change
66
+ agent.update_mcp_tool_cache_sync()
67
+ emit_info(
68
+ "Agent reloaded with updated servers",
69
+ message_group=group_id,
70
+ )
71
+ except Exception as e:
72
+ logger.warning(f"Could not reload agent: {e}")
73
+ else:
74
+ emit_info(
75
+ f"✗ Failed to stop server: {server_name}", message_group=group_id
76
+ )
77
+
78
+ except Exception as e:
79
+ logger.error(f"Error stopping server '{server_name}': {e}")
80
+ emit_error(f"Failed to stop server: {e}", message_group=group_id)