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,94 @@
1
+ """
2
+ MCP List Command - Lists all registered MCP servers in a formatted table.
3
+ """
4
+
5
+ import logging
6
+ from typing import List, Optional
7
+
8
+ from rich.table import Table
9
+ from rich.text import Text
10
+
11
+ from code_puppy.mcp_.managed_server import ServerState
12
+ from code_puppy.messaging import emit_error, emit_info
13
+
14
+ from .base import MCPCommandBase
15
+ from .utils import format_state_indicator, format_uptime
16
+
17
+ # Configure logging
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ListCommand(MCPCommandBase):
22
+ """
23
+ Command handler for listing MCP servers.
24
+
25
+ Displays all registered MCP servers in a formatted table with status information.
26
+ """
27
+
28
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
29
+ """
30
+ List all registered MCP servers in a formatted table.
31
+
32
+ Args:
33
+ args: Command arguments (unused for list command)
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 MCP servers registered", message_group=group_id)
44
+ return
45
+
46
+ # Create table for server list
47
+ table = Table(title="🔌 MCP Server Status Dashboard")
48
+ table.add_column("Name", style="cyan", no_wrap=True)
49
+ table.add_column("Type", style="dim", no_wrap=True)
50
+ table.add_column("State", justify="center")
51
+ table.add_column("Enabled", justify="center")
52
+ table.add_column("Uptime", style="dim")
53
+ table.add_column("Status", style="dim")
54
+
55
+ for server in servers:
56
+ # Format state with appropriate color and icon
57
+ state_display = format_state_indicator(server.state)
58
+
59
+ # Format enabled status
60
+ enabled_display = "✓" if server.enabled else "✗"
61
+ enabled_style = "green" if server.enabled else "red"
62
+
63
+ # Format uptime
64
+ uptime_display = format_uptime(server.uptime_seconds)
65
+
66
+ # Format status message
67
+ status_display = server.error_message or "OK"
68
+ if server.quarantined:
69
+ status_display = "Quarantined"
70
+
71
+ table.add_row(
72
+ server.name,
73
+ server.type.upper(),
74
+ state_display,
75
+ Text(enabled_display, style=enabled_style),
76
+ uptime_display,
77
+ status_display,
78
+ )
79
+
80
+ emit_info(table, message_group=group_id)
81
+
82
+ # Show summary
83
+ total = len(servers)
84
+ running = sum(
85
+ 1 for s in servers if s.state == ServerState.RUNNING and s.enabled
86
+ )
87
+ emit_info(
88
+ f"\n📊 Summary: {running}/{total} servers running",
89
+ message_group=group_id,
90
+ )
91
+
92
+ except Exception as e:
93
+ logger.error(f"Error listing MCP servers: {e}")
94
+ emit_error(f"Error listing servers: {e}", message_group=group_id)
@@ -0,0 +1,235 @@
1
+ """
2
+ MCP Logs Command - Shows server logs from persistent log files.
3
+ """
4
+
5
+ import logging
6
+ from typing import List, Optional
7
+
8
+ from rich.panel import Panel
9
+ from rich.syntax import Syntax
10
+ from rich.text import Text
11
+
12
+ from code_puppy.mcp_.mcp_logs import (
13
+ clear_logs,
14
+ get_log_file_path,
15
+ get_log_stats,
16
+ list_servers_with_logs,
17
+ read_logs,
18
+ )
19
+ from code_puppy.messaging import emit_error, emit_info
20
+
21
+ from .base import MCPCommandBase
22
+ from .utils import find_server_id_by_name, suggest_similar_servers
23
+
24
+ # Configure logging
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class LogsCommand(MCPCommandBase):
29
+ """
30
+ Command handler for showing MCP server logs.
31
+
32
+ Shows logs from persistent log files stored in ~/.code_puppy/mcp_logs/.
33
+ """
34
+
35
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
36
+ """
37
+ Show logs for a server.
38
+
39
+ Usage:
40
+ /mcp logs - List servers with logs
41
+ /mcp logs <server_name> - Show last 50 lines
42
+ /mcp logs <server_name> 100 - Show last 100 lines
43
+ /mcp logs <server_name> all - Show all logs
44
+ /mcp logs <server_name> --clear - Clear logs for server
45
+
46
+ Args:
47
+ args: Command arguments
48
+ group_id: Optional message group ID for grouping related messages
49
+ """
50
+ if group_id is None:
51
+ group_id = self.generate_group_id()
52
+
53
+ # No args - list servers with logs
54
+ if not args:
55
+ self._list_servers_with_logs(group_id)
56
+ return
57
+
58
+ server_name = args[0]
59
+
60
+ # Check for --clear flag
61
+ if len(args) > 1 and args[1] == "--clear":
62
+ self._clear_logs(server_name, group_id)
63
+ return
64
+
65
+ # Determine number of lines
66
+ lines = 50 # Default
67
+ show_all = False
68
+
69
+ if len(args) > 1:
70
+ if args[1].lower() == "all":
71
+ show_all = True
72
+ else:
73
+ try:
74
+ lines = int(args[1])
75
+ if lines <= 0:
76
+ emit_info(
77
+ "Lines must be positive, using default: 50",
78
+ message_group=group_id,
79
+ )
80
+ lines = 50
81
+ except ValueError:
82
+ emit_info(
83
+ f"Invalid number '{args[1]}', using default: 50",
84
+ message_group=group_id,
85
+ )
86
+
87
+ self._show_logs(server_name, lines if not show_all else None, group_id)
88
+
89
+ def _list_servers_with_logs(self, group_id: str) -> None:
90
+ """List all servers that have log files."""
91
+ servers = list_servers_with_logs()
92
+
93
+ if not servers:
94
+ emit_info(
95
+ "📋 No MCP server logs found.\n"
96
+ "Logs are created when servers are started.",
97
+ message_group=group_id,
98
+ )
99
+ return
100
+
101
+ lines = ["📋 **Servers with logs:**\n"]
102
+
103
+ for server in servers:
104
+ stats = get_log_stats(server)
105
+ size_kb = stats["total_size_bytes"] / 1024
106
+ size_str = (
107
+ f"{size_kb:.1f} KB" if size_kb < 1024 else f"{size_kb / 1024:.1f} MB"
108
+ )
109
+ rotated = (
110
+ f" (+{stats['rotated_count']} rotated)"
111
+ if stats["rotated_count"]
112
+ else ""
113
+ )
114
+ lines.append(
115
+ f" • **{server}** - {stats['line_count']} lines, {size_str}{rotated}"
116
+ )
117
+
118
+ lines.append("\n**Usage:** `/mcp logs <server_name> [lines|all]`")
119
+
120
+ emit_info("\n".join(lines), message_group=group_id)
121
+
122
+ def _show_logs(self, server_name: str, lines: Optional[int], group_id: str) -> None:
123
+ """
124
+ Show logs for a specific server.
125
+
126
+ Args:
127
+ server_name: Name of the server
128
+ lines: Number of lines to show, or None for all
129
+ group_id: Message group ID
130
+ """
131
+ try:
132
+ # Verify server exists in manager
133
+ server_id = find_server_id_by_name(self.manager, server_name)
134
+ if not server_id:
135
+ # Server not configured, but might have logs from before
136
+ stats = get_log_stats(server_name)
137
+ if not stats["exists"]:
138
+ emit_info(
139
+ f"Server '{server_name}' not found and has no logs.",
140
+ message_group=group_id,
141
+ )
142
+ suggest_similar_servers(
143
+ self.manager, server_name, group_id=group_id
144
+ )
145
+ return
146
+
147
+ # Read logs
148
+ log_lines = read_logs(server_name, lines=lines)
149
+
150
+ if not log_lines:
151
+ emit_info(
152
+ f"📋 No logs found for server: **{server_name}**\n"
153
+ f"Log file: `{get_log_file_path(server_name)}`",
154
+ message_group=group_id,
155
+ )
156
+ return
157
+
158
+ # Get stats for header
159
+ stats = get_log_stats(server_name)
160
+ total_lines = stats["line_count"]
161
+ showing = len(log_lines)
162
+
163
+ # Format header
164
+ if lines is None:
165
+ header = f"📋 Logs for {server_name} (all {total_lines} lines)"
166
+ else:
167
+ header = (
168
+ f"📋 Logs for {server_name} (last {showing} of {total_lines} lines)"
169
+ )
170
+
171
+ # Format log content with syntax highlighting
172
+ log_content = "\n".join(log_lines)
173
+
174
+ # Create a panel with the logs
175
+ syntax = Syntax(
176
+ log_content,
177
+ "log",
178
+ theme="monokai",
179
+ word_wrap=True,
180
+ line_numbers=False,
181
+ )
182
+
183
+ panel = Panel(
184
+ syntax,
185
+ title=header,
186
+ subtitle=f"Log file: {get_log_file_path(server_name)}",
187
+ border_style="dim",
188
+ )
189
+
190
+ emit_info(panel, message_group=group_id)
191
+
192
+ # Show hint for more options
193
+ if lines is not None and showing < total_lines:
194
+ emit_info(
195
+ Text.from_markup(
196
+ f"[dim]💡 Use `/mcp logs {server_name} all` to see all logs, "
197
+ f"or `/mcp logs {server_name} <number>` for specific count[/dim]"
198
+ ),
199
+ message_group=group_id,
200
+ )
201
+
202
+ except Exception as e:
203
+ logger.error(f"Error getting logs for server '{server_name}': {e}")
204
+ emit_error(f"Error getting logs: {e}", message_group=group_id)
205
+
206
+ def _clear_logs(self, server_name: str, group_id: str) -> None:
207
+ """
208
+ Clear logs for a specific server.
209
+
210
+ Args:
211
+ server_name: Name of the server
212
+ group_id: Message group ID
213
+ """
214
+ try:
215
+ stats = get_log_stats(server_name)
216
+
217
+ if not stats["exists"] and stats["rotated_count"] == 0:
218
+ emit_info(
219
+ f"No logs to clear for server: {server_name}",
220
+ message_group=group_id,
221
+ )
222
+ return
223
+
224
+ # Clear the logs
225
+ clear_logs(server_name, include_rotated=True)
226
+
227
+ cleared_count = 1 + stats["rotated_count"]
228
+ emit_info(
229
+ f"🗑️ Cleared {cleared_count} log file(s) for **{server_name}**",
230
+ message_group=group_id,
231
+ )
232
+
233
+ except Exception as e:
234
+ logger.error(f"Error clearing logs for server '{server_name}': {e}")
235
+ emit_error(f"Error clearing logs: {e}", message_group=group_id)
@@ -0,0 +1,82 @@
1
+ """
2
+ MCP Remove Command - Removes an MCP server.
3
+ """
4
+
5
+ import json
6
+ import logging
7
+ import os
8
+ from typing import List, Optional
9
+
10
+ from code_puppy.messaging import emit_error, emit_info
11
+
12
+ from .base import MCPCommandBase
13
+ from .utils import find_server_id_by_name, suggest_similar_servers
14
+
15
+ # Configure logging
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class RemoveCommand(MCPCommandBase):
20
+ """
21
+ Command handler for removing MCP servers.
22
+
23
+ Removes a specific MCP server from the manager and configuration.
24
+ """
25
+
26
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
27
+ """
28
+ Remove an MCP server.
29
+
30
+ Args:
31
+ args: Command arguments, expects [server_name]
32
+ group_id: Optional message group ID for grouping related messages
33
+ """
34
+ if group_id is None:
35
+ group_id = self.generate_group_id()
36
+
37
+ if not args:
38
+ emit_info("Usage: /mcp remove <server_name>", message_group=group_id)
39
+ return
40
+
41
+ server_name = args[0]
42
+
43
+ try:
44
+ # Find server by name
45
+ server_id = find_server_id_by_name(self.manager, server_name)
46
+ if not server_id:
47
+ emit_info(f"Server '{server_name}' not found", message_group=group_id)
48
+ suggest_similar_servers(self.manager, server_name, group_id=group_id)
49
+ return
50
+
51
+ # Actually remove the server
52
+ success = self.manager.remove_server(server_id)
53
+
54
+ if success:
55
+ emit_info(f"✓ Removed server: {server_name}", message_group=group_id)
56
+
57
+ # Also remove from mcp_servers.json
58
+ from code_puppy.config import MCP_SERVERS_FILE
59
+
60
+ if os.path.exists(MCP_SERVERS_FILE):
61
+ try:
62
+ with open(MCP_SERVERS_FILE, "r") as f:
63
+ data = json.load(f)
64
+ servers = data.get("mcp_servers", {})
65
+
66
+ # Remove the server if it exists
67
+ if server_name in servers:
68
+ del servers[server_name]
69
+
70
+ # Save back
71
+ with open(MCP_SERVERS_FILE, "w") as f:
72
+ json.dump(data, f, indent=2)
73
+ except Exception as e:
74
+ logger.warning(f"Could not update mcp_servers.json: {e}")
75
+ else:
76
+ emit_info(
77
+ f"✗ Failed to remove server: {server_name}", message_group=group_id
78
+ )
79
+
80
+ except Exception as e:
81
+ logger.error(f"Error removing server '{server_name}': {e}")
82
+ emit_error(f"Error removing server: {e}", message_group=group_id)
@@ -0,0 +1,100 @@
1
+ """
2
+ MCP Restart Command - Restarts 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_info
11
+
12
+ from .base import MCPCommandBase
13
+ from .utils import find_server_id_by_name, suggest_similar_servers
14
+
15
+ # Configure logging
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class RestartCommand(MCPCommandBase):
20
+ """
21
+ Command handler for restarting MCP servers.
22
+
23
+ Stops, reloads configuration, and starts a specific MCP server.
24
+ """
25
+
26
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
27
+ """
28
+ Restart a specific MCP server.
29
+
30
+ Args:
31
+ args: Command arguments, expects [server_name]
32
+ group_id: Optional message group ID for grouping related messages
33
+ """
34
+ if group_id is None:
35
+ group_id = self.generate_group_id()
36
+
37
+ if not args:
38
+ emit_info("Usage: /mcp restart <server_name>", message_group=group_id)
39
+ return
40
+
41
+ server_name = args[0]
42
+
43
+ try:
44
+ # Find server by name
45
+ server_id = find_server_id_by_name(self.manager, server_name)
46
+ if not server_id:
47
+ emit_info(f"Server '{server_name}' not found", message_group=group_id)
48
+ suggest_similar_servers(self.manager, server_name, group_id=group_id)
49
+ return
50
+
51
+ # Stop the server first
52
+ emit_info(f"Stopping server: {server_name}", message_group=group_id)
53
+ self.manager.stop_server_sync(server_id)
54
+
55
+ # Then reload and start it
56
+ emit_info("Reloading configuration...", message_group=group_id)
57
+ reload_success = self.manager.reload_server(server_id)
58
+
59
+ if reload_success:
60
+ emit_info(f"Starting server: {server_name}", message_group=group_id)
61
+ start_success = self.manager.start_server_sync(server_id)
62
+
63
+ if start_success:
64
+ emit_info(
65
+ f"✓ Restarted server: {server_name}", message_group=group_id
66
+ )
67
+
68
+ # Reload the agent to pick up the server changes
69
+ try:
70
+ from code_puppy.agents import get_current_agent
71
+
72
+ agent = get_current_agent()
73
+ agent.reload_code_generation_agent()
74
+ # Update MCP tool cache immediately so token counts reflect the change
75
+ agent.update_mcp_tool_cache_sync()
76
+ emit_info(
77
+ Text.from_markup(
78
+ "[dim]Agent reloaded with updated servers[/dim]"
79
+ ),
80
+ message_group=group_id,
81
+ )
82
+ except Exception as e:
83
+ logger.warning(f"Could not reload agent: {e}")
84
+ else:
85
+ emit_info(
86
+ f"✗ Failed to start server after reload: {server_name}",
87
+ message_group=group_id,
88
+ )
89
+ else:
90
+ emit_info(
91
+ f"✗ Failed to reload server configuration: {server_name}",
92
+ message_group=group_id,
93
+ )
94
+
95
+ except Exception as e:
96
+ logger.error(f"Error restarting server '{server_name}': {e}")
97
+ emit_info(
98
+ Text.from_markup(f"[red]Failed to restart server: {e}[/red]"),
99
+ message_group=group_id,
100
+ )
@@ -0,0 +1,123 @@
1
+ """
2
+ MCP Search Command - Searches for pre-configured MCP servers in the registry.
3
+ """
4
+
5
+ import logging
6
+ from typing import List, Optional
7
+
8
+ from rich.table import Table
9
+ from rich.text import Text
10
+
11
+ from code_puppy.messaging import emit_info, emit_system_message, emit_warning
12
+
13
+ from .base import MCPCommandBase
14
+
15
+ # Configure logging
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class SearchCommand(MCPCommandBase):
20
+ """
21
+ Command handler for searching MCP server registry.
22
+
23
+ Searches for pre-configured MCP servers with optional query terms.
24
+ """
25
+
26
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
27
+ """
28
+ Search for pre-configured MCP servers in the registry.
29
+
30
+ Args:
31
+ args: Search query terms
32
+ group_id: Optional message group ID for grouping related messages
33
+ """
34
+ if group_id is None:
35
+ group_id = self.generate_group_id()
36
+
37
+ try:
38
+ from code_puppy.mcp_.server_registry_catalog import catalog
39
+
40
+ if not args:
41
+ # Show popular servers if no query
42
+ emit_info(
43
+ "Popular MCP Servers:\n",
44
+ message_group=group_id,
45
+ )
46
+ servers = catalog.get_popular(15)
47
+ else:
48
+ query = " ".join(args)
49
+ emit_info(
50
+ f"Searching for: {query}\n",
51
+ message_group=group_id,
52
+ )
53
+ servers = catalog.search(query)
54
+
55
+ if not servers:
56
+ emit_warning(
57
+ "No servers found matching your search",
58
+ message_group=group_id,
59
+ )
60
+ emit_info(
61
+ "Try: /mcp search database, /mcp search file, /mcp search git",
62
+ message_group=group_id,
63
+ )
64
+ return
65
+
66
+ # Create results table
67
+ table = Table(show_header=True, header_style="bold magenta")
68
+ table.add_column("ID", style="cyan", width=20)
69
+ table.add_column("Name", style="green")
70
+ table.add_column("Category", style="yellow")
71
+ table.add_column("Description", style="white")
72
+ table.add_column("Tags", style="dim")
73
+
74
+ for server in servers[:20]: # Limit to 20 results
75
+ tags = ", ".join(server.tags[:3]) # Show first 3 tags
76
+ if len(server.tags) > 3:
77
+ tags += "..."
78
+
79
+ # Add verified/popular indicators
80
+ indicators = []
81
+ if server.verified:
82
+ indicators.append("✓")
83
+ if server.popular:
84
+ indicators.append("⭐")
85
+ name_display = server.display_name
86
+ if indicators:
87
+ name_display += f" {''.join(indicators)}"
88
+
89
+ table.add_row(
90
+ server.id,
91
+ name_display,
92
+ server.category,
93
+ server.description[:50] + "..."
94
+ if len(server.description) > 50
95
+ else server.description,
96
+ tags,
97
+ )
98
+
99
+ # The first message established the group, subsequent messages will auto-group
100
+ emit_system_message(table, message_group=group_id)
101
+ emit_info("\n✓ = Verified ⭐ = Popular", message_group=group_id)
102
+ emit_info(
103
+ Text.from_markup("[yellow]To install:[/yellow] /mcp install <id>"),
104
+ message_group=group_id,
105
+ )
106
+ emit_info(
107
+ Text.from_markup(
108
+ "[yellow]For details:[/yellow] /mcp search <specific-term>"
109
+ ),
110
+ message_group=group_id,
111
+ )
112
+
113
+ except ImportError:
114
+ emit_info(
115
+ Text.from_markup("[red]Server registry not available[/red]"),
116
+ message_group=group_id,
117
+ )
118
+ except Exception as e:
119
+ logger.error(f"Error searching server registry: {e}")
120
+ emit_info(
121
+ Text.from_markup(f"[red]Error searching servers: {e}[/red]"),
122
+ message_group=group_id,
123
+ )