code-puppy 0.0.214__py3-none-any.whl → 0.0.366__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 (231) hide show
  1. code_puppy/__init__.py +7 -1
  2. code_puppy/agents/__init__.py +2 -0
  3. code_puppy/agents/agent_c_reviewer.py +59 -6
  4. code_puppy/agents/agent_code_puppy.py +7 -1
  5. code_puppy/agents/agent_code_reviewer.py +12 -2
  6. code_puppy/agents/agent_cpp_reviewer.py +73 -6
  7. code_puppy/agents/agent_creator_agent.py +45 -4
  8. code_puppy/agents/agent_golang_reviewer.py +92 -3
  9. code_puppy/agents/agent_javascript_reviewer.py +101 -8
  10. code_puppy/agents/agent_manager.py +81 -4
  11. code_puppy/agents/agent_pack_leader.py +383 -0
  12. code_puppy/agents/agent_planning.py +163 -0
  13. code_puppy/agents/agent_python_programmer.py +165 -0
  14. code_puppy/agents/agent_python_reviewer.py +28 -6
  15. code_puppy/agents/agent_qa_expert.py +98 -6
  16. code_puppy/agents/agent_qa_kitten.py +12 -7
  17. code_puppy/agents/agent_security_auditor.py +113 -3
  18. code_puppy/agents/agent_terminal_qa.py +323 -0
  19. code_puppy/agents/agent_typescript_reviewer.py +106 -7
  20. code_puppy/agents/base_agent.py +802 -176
  21. code_puppy/agents/event_stream_handler.py +350 -0
  22. code_puppy/agents/pack/__init__.py +34 -0
  23. code_puppy/agents/pack/bloodhound.py +304 -0
  24. code_puppy/agents/pack/husky.py +321 -0
  25. code_puppy/agents/pack/retriever.py +393 -0
  26. code_puppy/agents/pack/shepherd.py +348 -0
  27. code_puppy/agents/pack/terrier.py +287 -0
  28. code_puppy/agents/pack/watchdog.py +367 -0
  29. code_puppy/agents/prompt_reviewer.py +145 -0
  30. code_puppy/agents/subagent_stream_handler.py +276 -0
  31. code_puppy/api/__init__.py +13 -0
  32. code_puppy/api/app.py +169 -0
  33. code_puppy/api/main.py +21 -0
  34. code_puppy/api/pty_manager.py +446 -0
  35. code_puppy/api/routers/__init__.py +12 -0
  36. code_puppy/api/routers/agents.py +36 -0
  37. code_puppy/api/routers/commands.py +217 -0
  38. code_puppy/api/routers/config.py +74 -0
  39. code_puppy/api/routers/sessions.py +232 -0
  40. code_puppy/api/templates/terminal.html +361 -0
  41. code_puppy/api/websocket.py +154 -0
  42. code_puppy/callbacks.py +142 -4
  43. code_puppy/chatgpt_codex_client.py +283 -0
  44. code_puppy/claude_cache_client.py +586 -0
  45. code_puppy/cli_runner.py +916 -0
  46. code_puppy/command_line/add_model_menu.py +1079 -0
  47. code_puppy/command_line/agent_menu.py +395 -0
  48. code_puppy/command_line/attachments.py +10 -5
  49. code_puppy/command_line/autosave_menu.py +605 -0
  50. code_puppy/command_line/clipboard.py +527 -0
  51. code_puppy/command_line/colors_menu.py +520 -0
  52. code_puppy/command_line/command_handler.py +176 -738
  53. code_puppy/command_line/command_registry.py +150 -0
  54. code_puppy/command_line/config_commands.py +715 -0
  55. code_puppy/command_line/core_commands.py +792 -0
  56. code_puppy/command_line/diff_menu.py +863 -0
  57. code_puppy/command_line/load_context_completion.py +15 -22
  58. code_puppy/command_line/mcp/base.py +0 -3
  59. code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
  60. code_puppy/command_line/mcp/custom_server_form.py +688 -0
  61. code_puppy/command_line/mcp/custom_server_installer.py +195 -0
  62. code_puppy/command_line/mcp/edit_command.py +148 -0
  63. code_puppy/command_line/mcp/handler.py +9 -4
  64. code_puppy/command_line/mcp/help_command.py +6 -5
  65. code_puppy/command_line/mcp/install_command.py +15 -26
  66. code_puppy/command_line/mcp/install_menu.py +685 -0
  67. code_puppy/command_line/mcp/list_command.py +2 -2
  68. code_puppy/command_line/mcp/logs_command.py +174 -65
  69. code_puppy/command_line/mcp/remove_command.py +2 -2
  70. code_puppy/command_line/mcp/restart_command.py +12 -4
  71. code_puppy/command_line/mcp/search_command.py +16 -10
  72. code_puppy/command_line/mcp/start_all_command.py +18 -6
  73. code_puppy/command_line/mcp/start_command.py +47 -25
  74. code_puppy/command_line/mcp/status_command.py +4 -5
  75. code_puppy/command_line/mcp/stop_all_command.py +7 -1
  76. code_puppy/command_line/mcp/stop_command.py +8 -4
  77. code_puppy/command_line/mcp/test_command.py +2 -2
  78. code_puppy/command_line/mcp/wizard_utils.py +20 -16
  79. code_puppy/command_line/mcp_completion.py +174 -0
  80. code_puppy/command_line/model_picker_completion.py +75 -25
  81. code_puppy/command_line/model_settings_menu.py +884 -0
  82. code_puppy/command_line/motd.py +14 -8
  83. code_puppy/command_line/onboarding_slides.py +179 -0
  84. code_puppy/command_line/onboarding_wizard.py +340 -0
  85. code_puppy/command_line/pin_command_completion.py +329 -0
  86. code_puppy/command_line/prompt_toolkit_completion.py +463 -63
  87. code_puppy/command_line/session_commands.py +296 -0
  88. code_puppy/command_line/utils.py +54 -0
  89. code_puppy/config.py +898 -112
  90. code_puppy/error_logging.py +118 -0
  91. code_puppy/gemini_code_assist.py +385 -0
  92. code_puppy/gemini_model.py +602 -0
  93. code_puppy/http_utils.py +210 -148
  94. code_puppy/keymap.py +128 -0
  95. code_puppy/main.py +5 -698
  96. code_puppy/mcp_/__init__.py +17 -0
  97. code_puppy/mcp_/async_lifecycle.py +35 -4
  98. code_puppy/mcp_/blocking_startup.py +70 -43
  99. code_puppy/mcp_/captured_stdio_server.py +2 -2
  100. code_puppy/mcp_/config_wizard.py +4 -4
  101. code_puppy/mcp_/dashboard.py +15 -6
  102. code_puppy/mcp_/managed_server.py +65 -38
  103. code_puppy/mcp_/manager.py +146 -52
  104. code_puppy/mcp_/mcp_logs.py +224 -0
  105. code_puppy/mcp_/registry.py +6 -6
  106. code_puppy/mcp_/server_registry_catalog.py +24 -5
  107. code_puppy/messaging/__init__.py +199 -2
  108. code_puppy/messaging/bus.py +610 -0
  109. code_puppy/messaging/commands.py +167 -0
  110. code_puppy/messaging/markdown_patches.py +57 -0
  111. code_puppy/messaging/message_queue.py +17 -48
  112. code_puppy/messaging/messages.py +500 -0
  113. code_puppy/messaging/queue_console.py +1 -24
  114. code_puppy/messaging/renderers.py +43 -146
  115. code_puppy/messaging/rich_renderer.py +1027 -0
  116. code_puppy/messaging/spinner/__init__.py +21 -5
  117. code_puppy/messaging/spinner/console_spinner.py +86 -51
  118. code_puppy/messaging/subagent_console.py +461 -0
  119. code_puppy/model_factory.py +634 -83
  120. code_puppy/model_utils.py +167 -0
  121. code_puppy/models.json +66 -68
  122. code_puppy/models_dev_api.json +1 -0
  123. code_puppy/models_dev_parser.py +592 -0
  124. code_puppy/plugins/__init__.py +164 -10
  125. code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
  126. code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
  127. code_puppy/plugins/antigravity_oauth/antigravity_model.py +704 -0
  128. code_puppy/plugins/antigravity_oauth/config.py +42 -0
  129. code_puppy/plugins/antigravity_oauth/constants.py +136 -0
  130. code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
  131. code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
  132. code_puppy/plugins/antigravity_oauth/storage.py +271 -0
  133. code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
  134. code_puppy/plugins/antigravity_oauth/token.py +167 -0
  135. code_puppy/plugins/antigravity_oauth/transport.py +767 -0
  136. code_puppy/plugins/antigravity_oauth/utils.py +169 -0
  137. code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
  138. code_puppy/plugins/chatgpt_oauth/config.py +52 -0
  139. code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
  140. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +94 -0
  141. code_puppy/plugins/chatgpt_oauth/test_plugin.py +293 -0
  142. code_puppy/plugins/chatgpt_oauth/utils.py +489 -0
  143. code_puppy/plugins/claude_code_oauth/README.md +167 -0
  144. code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
  145. code_puppy/plugins/claude_code_oauth/__init__.py +6 -0
  146. code_puppy/plugins/claude_code_oauth/config.py +50 -0
  147. code_puppy/plugins/claude_code_oauth/register_callbacks.py +308 -0
  148. code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
  149. code_puppy/plugins/claude_code_oauth/utils.py +518 -0
  150. code_puppy/plugins/customizable_commands/__init__.py +0 -0
  151. code_puppy/plugins/customizable_commands/register_callbacks.py +169 -0
  152. code_puppy/plugins/example_custom_command/README.md +280 -0
  153. code_puppy/plugins/example_custom_command/register_callbacks.py +2 -2
  154. code_puppy/plugins/file_permission_handler/__init__.py +4 -0
  155. code_puppy/plugins/file_permission_handler/register_callbacks.py +523 -0
  156. code_puppy/plugins/frontend_emitter/__init__.py +25 -0
  157. code_puppy/plugins/frontend_emitter/emitter.py +121 -0
  158. code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
  159. code_puppy/plugins/oauth_puppy_html.py +228 -0
  160. code_puppy/plugins/shell_safety/__init__.py +6 -0
  161. code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
  162. code_puppy/plugins/shell_safety/command_cache.py +156 -0
  163. code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
  164. code_puppy/prompts/antigravity_system_prompt.md +1 -0
  165. code_puppy/prompts/codex_system_prompt.md +310 -0
  166. code_puppy/pydantic_patches.py +131 -0
  167. code_puppy/reopenable_async_client.py +8 -8
  168. code_puppy/round_robin_model.py +9 -12
  169. code_puppy/session_storage.py +2 -1
  170. code_puppy/status_display.py +21 -4
  171. code_puppy/summarization_agent.py +41 -13
  172. code_puppy/terminal_utils.py +418 -0
  173. code_puppy/tools/__init__.py +37 -1
  174. code_puppy/tools/agent_tools.py +536 -52
  175. code_puppy/tools/browser/__init__.py +37 -0
  176. code_puppy/tools/browser/browser_control.py +19 -23
  177. code_puppy/tools/browser/browser_interactions.py +41 -48
  178. code_puppy/tools/browser/browser_locators.py +36 -38
  179. code_puppy/tools/browser/browser_manager.py +316 -0
  180. code_puppy/tools/browser/browser_navigation.py +16 -16
  181. code_puppy/tools/browser/browser_screenshot.py +79 -143
  182. code_puppy/tools/browser/browser_scripts.py +32 -42
  183. code_puppy/tools/browser/browser_workflows.py +44 -27
  184. code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
  185. code_puppy/tools/browser/terminal_command_tools.py +521 -0
  186. code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
  187. code_puppy/tools/browser/terminal_tools.py +525 -0
  188. code_puppy/tools/command_runner.py +930 -147
  189. code_puppy/tools/common.py +1113 -5
  190. code_puppy/tools/display.py +84 -0
  191. code_puppy/tools/file_modifications.py +288 -89
  192. code_puppy/tools/file_operations.py +226 -154
  193. code_puppy/tools/subagent_context.py +158 -0
  194. code_puppy/uvx_detection.py +242 -0
  195. code_puppy/version_checker.py +30 -11
  196. code_puppy-0.0.366.data/data/code_puppy/models.json +110 -0
  197. code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json +1 -0
  198. {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/METADATA +149 -75
  199. code_puppy-0.0.366.dist-info/RECORD +217 -0
  200. {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/WHEEL +1 -1
  201. code_puppy/command_line/mcp/add_command.py +0 -183
  202. code_puppy/messaging/spinner/textual_spinner.py +0 -106
  203. code_puppy/tools/browser/camoufox_manager.py +0 -216
  204. code_puppy/tools/browser/vqa_agent.py +0 -70
  205. code_puppy/tui/__init__.py +0 -10
  206. code_puppy/tui/app.py +0 -1105
  207. code_puppy/tui/components/__init__.py +0 -21
  208. code_puppy/tui/components/chat_view.py +0 -551
  209. code_puppy/tui/components/command_history_modal.py +0 -218
  210. code_puppy/tui/components/copy_button.py +0 -139
  211. code_puppy/tui/components/custom_widgets.py +0 -63
  212. code_puppy/tui/components/human_input_modal.py +0 -175
  213. code_puppy/tui/components/input_area.py +0 -167
  214. code_puppy/tui/components/sidebar.py +0 -309
  215. code_puppy/tui/components/status_bar.py +0 -185
  216. code_puppy/tui/messages.py +0 -27
  217. code_puppy/tui/models/__init__.py +0 -8
  218. code_puppy/tui/models/chat_message.py +0 -25
  219. code_puppy/tui/models/command_history.py +0 -89
  220. code_puppy/tui/models/enums.py +0 -24
  221. code_puppy/tui/screens/__init__.py +0 -17
  222. code_puppy/tui/screens/autosave_picker.py +0 -175
  223. code_puppy/tui/screens/help.py +0 -130
  224. code_puppy/tui/screens/mcp_install_wizard.py +0 -803
  225. code_puppy/tui/screens/settings.py +0 -306
  226. code_puppy/tui/screens/tools.py +0 -74
  227. code_puppy/tui_state.py +0 -55
  228. code_puppy-0.0.214.data/data/code_puppy/models.json +0 -112
  229. code_puppy-0.0.214.dist-info/RECORD +0 -131
  230. {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/entry_points.txt +0 -0
  231. {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,195 @@
1
+ """Custom MCP server installation logic.
2
+
3
+ Handles prompting users for custom server configuration and installing
4
+ custom MCP servers with JSON configuration.
5
+ """
6
+
7
+ import json
8
+ import os
9
+
10
+ from code_puppy.command_line.utils import safe_input
11
+ from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
12
+
13
+ # Example configurations for each server type
14
+ CUSTOM_SERVER_EXAMPLES = {
15
+ "stdio": """{
16
+ "type": "stdio",
17
+ "command": "npx",
18
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
19
+ "env": {
20
+ "NODE_ENV": "production"
21
+ },
22
+ "timeout": 30
23
+ }""",
24
+ "http": """{
25
+ "type": "http",
26
+ "url": "http://localhost:8080/mcp",
27
+ "headers": {
28
+ "Authorization": "Bearer $MY_API_KEY",
29
+ "Content-Type": "application/json"
30
+ },
31
+ "timeout": 30
32
+ }""",
33
+ "sse": """{
34
+ "type": "sse",
35
+ "url": "http://localhost:8080/sse",
36
+ "headers": {
37
+ "Authorization": "Bearer $MY_API_KEY"
38
+ }
39
+ }""",
40
+ }
41
+
42
+
43
+ def prompt_and_install_custom_server(manager) -> bool:
44
+ """Prompt for custom server configuration and install it.
45
+
46
+ Args:
47
+ manager: MCP manager instance
48
+
49
+ Returns:
50
+ True if successful, False otherwise
51
+ """
52
+ from code_puppy.config import MCP_SERVERS_FILE
53
+ from code_puppy.mcp_.managed_server import ServerConfig
54
+
55
+ from .utils import find_server_id_by_name
56
+
57
+ emit_info("\n➕ Add Custom MCP Server\n")
58
+ emit_info(" Configure your own MCP server using JSON.\n")
59
+
60
+ # Get server name
61
+ try:
62
+ server_name = safe_input(" Server name: ")
63
+ if not server_name:
64
+ emit_warning("Server name is required")
65
+ return False
66
+ except (KeyboardInterrupt, EOFError):
67
+ emit_info("")
68
+ emit_warning("Cancelled")
69
+ return False
70
+
71
+ # Check if server already exists
72
+ existing = find_server_id_by_name(manager, server_name)
73
+ if existing:
74
+ try:
75
+ override = safe_input(f" Server '{server_name}' exists. Override? [y/N]: ")
76
+ if not override.lower().startswith("y"):
77
+ emit_warning("Cancelled")
78
+ return False
79
+ except (KeyboardInterrupt, EOFError):
80
+ emit_info("")
81
+ emit_warning("Cancelled")
82
+ return False
83
+
84
+ # Select server type
85
+ emit_info("\n Select server type:\n")
86
+ emit_info(" 1. 📟 stdio - Local command (npx, python, uvx, etc.)")
87
+ emit_info(" 2. 🌐 http - HTTP endpoint")
88
+ emit_info(" 3. 📡 sse - Server-Sent Events\n")
89
+
90
+ try:
91
+ type_choice = safe_input(" Enter choice [1-3]: ")
92
+ except (KeyboardInterrupt, EOFError):
93
+ emit_info("")
94
+ emit_warning("Cancelled")
95
+ return False
96
+
97
+ type_map = {"1": "stdio", "2": "http", "3": "sse"}
98
+ server_type = type_map.get(type_choice)
99
+ if not server_type:
100
+ emit_warning("Invalid choice")
101
+ return False
102
+
103
+ # Show example for selected type
104
+ example = CUSTOM_SERVER_EXAMPLES.get(server_type, "{}")
105
+ emit_info(f"\n Example {server_type} configuration:\n")
106
+ for line in example.split("\n"):
107
+ emit_info(f" {line}")
108
+ emit_info("")
109
+
110
+ # Get JSON configuration
111
+ emit_info(" Enter your JSON configuration (paste and press Enter twice):\n")
112
+
113
+ json_lines = []
114
+ empty_count = 0
115
+ try:
116
+ while True:
117
+ line = safe_input("")
118
+ if line == "":
119
+ empty_count += 1
120
+ if empty_count >= 2:
121
+ break
122
+ json_lines.append(line)
123
+ else:
124
+ empty_count = 0
125
+ json_lines.append(line)
126
+ except (KeyboardInterrupt, EOFError):
127
+ emit_info("")
128
+ emit_warning("Cancelled")
129
+ return False
130
+
131
+ json_str = "\n".join(json_lines).strip()
132
+ if not json_str:
133
+ emit_warning("No configuration provided")
134
+ return False
135
+
136
+ # Parse JSON
137
+ try:
138
+ config_dict = json.loads(json_str)
139
+ except json.JSONDecodeError as e:
140
+ emit_error(f"Invalid JSON: {e}")
141
+ return False
142
+
143
+ # Validate required fields based on type
144
+ if server_type == "stdio":
145
+ if "command" not in config_dict:
146
+ emit_error("stdio servers require a 'command' field")
147
+ return False
148
+ elif server_type in ("http", "sse"):
149
+ if "url" not in config_dict:
150
+ emit_error(f"{server_type} servers require a 'url' field")
151
+ return False
152
+
153
+ # Create server config
154
+ try:
155
+ server_config = ServerConfig(
156
+ id=server_name,
157
+ name=server_name,
158
+ type=server_type,
159
+ enabled=True,
160
+ config=config_dict,
161
+ )
162
+
163
+ # Register with manager
164
+ server_id = manager.register_server(server_config)
165
+
166
+ if not server_id:
167
+ emit_error("Failed to register server")
168
+ return False
169
+
170
+ # Save to mcp_servers.json for persistence
171
+ if os.path.exists(MCP_SERVERS_FILE):
172
+ with open(MCP_SERVERS_FILE, "r") as f:
173
+ data = json.load(f)
174
+ servers = data.get("mcp_servers", {})
175
+ else:
176
+ servers = {}
177
+ data = {"mcp_servers": servers}
178
+
179
+ # Add new server with type
180
+ save_config = config_dict.copy()
181
+ save_config["type"] = server_type
182
+ servers[server_name] = save_config
183
+
184
+ # Save back
185
+ os.makedirs(os.path.dirname(MCP_SERVERS_FILE), exist_ok=True)
186
+ with open(MCP_SERVERS_FILE, "w") as f:
187
+ json.dump(data, f, indent=2)
188
+
189
+ emit_success(f"\n ✅ Successfully added custom server '{server_name}'!")
190
+ emit_info(f" Use '/mcp start {server_name}' to start the server.\n")
191
+ return True
192
+
193
+ except Exception as e:
194
+ emit_error(f"Failed to add server: {e}")
195
+ return False
@@ -0,0 +1,148 @@
1
+ """MCP Edit Command - Edit existing MCP server configurations.
2
+
3
+ Provides a TUI for editing custom MCP server configurations.
4
+ """
5
+
6
+ import json
7
+ import logging
8
+ import os
9
+ from typing import List, Optional
10
+
11
+ from rich.text import Text
12
+
13
+ from code_puppy.config import MCP_SERVERS_FILE
14
+ from code_puppy.messaging import emit_error, emit_info, emit_warning
15
+
16
+ from .base import MCPCommandBase
17
+ from .custom_server_form import run_custom_server_form
18
+
19
+ # Configure logging
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class EditCommand(MCPCommandBase):
24
+ """Command handler for editing existing MCP servers.
25
+
26
+ Opens the same TUI form as /mcp install custom, but pre-populated
27
+ with the existing server's configuration.
28
+ """
29
+
30
+ def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
31
+ """Edit an existing MCP server configuration.
32
+
33
+ Args:
34
+ args: Server name to edit
35
+ group_id: Optional message group ID for grouping related messages
36
+ """
37
+ if group_id is None:
38
+ group_id = self.generate_group_id()
39
+
40
+ try:
41
+ # Need a server name
42
+ if not args:
43
+ emit_info(
44
+ Text.from_markup("[yellow]Usage: /mcp edit <server_name>[/yellow]"),
45
+ message_group=group_id,
46
+ )
47
+ emit_info(
48
+ "Use '/mcp list' to see available servers.",
49
+ message_group=group_id,
50
+ )
51
+ return
52
+
53
+ server_name = args[0]
54
+
55
+ # Load existing server config
56
+ server_config = self._load_server_config(server_name, group_id)
57
+ if server_config is None:
58
+ return
59
+
60
+ server_type, config_dict = server_config
61
+
62
+ # Run the form in edit mode
63
+ success = run_custom_server_form(
64
+ self.manager,
65
+ edit_mode=True,
66
+ existing_name=server_name,
67
+ existing_type=server_type,
68
+ existing_config=config_dict,
69
+ )
70
+
71
+ if success:
72
+ # Reload MCP servers to pick up changes
73
+ try:
74
+ from code_puppy.agent import reload_mcp_servers
75
+
76
+ reload_mcp_servers()
77
+ except ImportError:
78
+ pass
79
+
80
+ except Exception as e:
81
+ logger.error(f"Error editing server: {e}")
82
+ emit_error(f"Error: {e}", message_group=group_id)
83
+
84
+ def _load_server_config(
85
+ self, server_name: str, group_id: str
86
+ ) -> Optional[tuple[str, dict]]:
87
+ """Load an existing server configuration from mcp_servers.json.
88
+
89
+ Args:
90
+ server_name: Name of the server to load
91
+ group_id: Message group ID for output
92
+
93
+ Returns:
94
+ Tuple of (server_type, config_dict) or None if not found
95
+ """
96
+ if not os.path.exists(MCP_SERVERS_FILE):
97
+ emit_error(
98
+ "No MCP servers configured yet.",
99
+ message_group=group_id,
100
+ )
101
+ emit_info(
102
+ "Use '/mcp install' to add a server first.",
103
+ message_group=group_id,
104
+ )
105
+ return None
106
+
107
+ try:
108
+ with open(MCP_SERVERS_FILE, "r") as f:
109
+ data = json.load(f)
110
+
111
+ servers = data.get("mcp_servers", {})
112
+
113
+ if server_name not in servers:
114
+ emit_error(
115
+ f"Server '{server_name}' not found.",
116
+ message_group=group_id,
117
+ )
118
+ # Show available servers
119
+ if servers:
120
+ emit_warning(
121
+ "\nAvailable servers:",
122
+ message_group=group_id,
123
+ )
124
+ for name in sorted(servers.keys()):
125
+ emit_info(f" • {name}", message_group=group_id)
126
+ return None
127
+
128
+ config = servers[
129
+ server_name
130
+ ].copy() # Make a copy to avoid modifying original
131
+
132
+ # Extract type from config (default to stdio)
133
+ server_type = config.pop("type", "stdio")
134
+
135
+ return (server_type, config)
136
+
137
+ except json.JSONDecodeError as e:
138
+ emit_error(
139
+ f"Error reading config file: {e}",
140
+ message_group=group_id,
141
+ )
142
+ return None
143
+ except Exception as e:
144
+ emit_error(
145
+ f"Error loading server config: {e}",
146
+ message_group=group_id,
147
+ )
148
+ return None
@@ -8,10 +8,12 @@ to their respective command modules.
8
8
  import logging
9
9
  import shlex
10
10
 
11
+ from rich.text import Text
12
+
11
13
  from code_puppy.messaging import emit_info
12
14
 
13
- from .add_command import AddCommand
14
15
  from .base import MCPCommandBase
16
+ from .edit_command import EditCommand
15
17
  from .help_command import HelpCommand
16
18
  from .install_command import InstallCommand
17
19
 
@@ -60,7 +62,7 @@ class MCPCommandHandler(MCPCommandBase):
60
62
  "restart": RestartCommand(),
61
63
  "status": StatusCommand(),
62
64
  "test": TestCommand(),
63
- "add": AddCommand(),
65
+ "edit": EditCommand(),
64
66
  "remove": RemoveCommand(),
65
67
  "logs": LogsCommand(),
66
68
  "search": SearchCommand(),
@@ -101,7 +103,8 @@ class MCPCommandHandler(MCPCommandBase):
101
103
  args = shlex.split(args_str)
102
104
  except ValueError as e:
103
105
  emit_info(
104
- f"[red]Invalid command syntax: {e}[/red]", message_group=group_id
106
+ Text.from_markup(f"[red]Invalid command syntax: {e}[/red]"),
107
+ message_group=group_id,
105
108
  )
106
109
  return True
107
110
 
@@ -119,7 +122,9 @@ class MCPCommandHandler(MCPCommandBase):
119
122
  return True
120
123
  else:
121
124
  emit_info(
122
- f"[yellow]Unknown MCP subcommand: {subcommand}[/yellow]",
125
+ Text.from_markup(
126
+ f"[yellow]Unknown MCP subcommand: {subcommand}[/yellow]"
127
+ ),
123
128
  message_group=group_id,
124
129
  )
125
130
  emit_info(
@@ -7,7 +7,7 @@ from typing import List, Optional
7
7
 
8
8
  from rich.text import Text
9
9
 
10
- from code_puppy.messaging import emit_info
10
+ from code_puppy.messaging import emit_error, emit_info
11
11
 
12
12
  from .base import MCPCommandBase
13
13
 
@@ -102,8 +102,8 @@ class HelpCommand(MCPCommandBase):
102
102
  + Text(" <name> [limit] Show recent events (default limit: 10)")
103
103
  )
104
104
  help_lines.append(
105
- Text("/mcp add", style="cyan")
106
- + Text(" [json] Add new server (JSON or wizard)")
105
+ Text("/mcp edit", style="cyan")
106
+ + Text(" <name> Edit existing server config")
107
107
  )
108
108
  help_lines.append(
109
109
  Text("/mcp remove", style="cyan")
@@ -129,7 +129,8 @@ class HelpCommand(MCPCommandBase):
129
129
  /mcp start filesystem # Start a specific server
130
130
  /mcp start-all # Start all servers at once
131
131
  /mcp stop-all # Stop all running servers
132
- /mcp add {"name": "test", "type": "stdio", "command": "echo"}"""
132
+ /mcp edit filesystem # Edit an existing server config
133
+ /mcp remove filesystem # Remove a server"""
133
134
  help_lines.append(Text(examples_text, style="dim"))
134
135
 
135
136
  # Combine all lines
@@ -143,4 +144,4 @@ class HelpCommand(MCPCommandBase):
143
144
 
144
145
  except Exception as e:
145
146
  logger.error(f"Error showing help: {e}")
146
- emit_info(f"[red]Error showing help: {e}[/red]", message_group=group_id)
147
+ emit_error(f"Error showing help: {e}", message_group=group_id)
@@ -5,11 +5,12 @@ MCP Install Command - Installs pre-configured MCP servers from the registry.
5
5
  import logging
6
6
  from typing import List, Optional
7
7
 
8
- from code_puppy.messaging import emit_info
9
- from code_puppy.tui_state import is_tui_mode
8
+ from rich.text import Text
9
+
10
+ from code_puppy.messaging import emit_error, emit_info
10
11
 
11
12
  from .base import MCPCommandBase
12
- from .wizard_utils import run_interactive_install_wizard
13
+ from .install_menu import run_mcp_install_menu
13
14
 
14
15
  # Configure logging
15
16
  logger = logging.getLogger(__name__)
@@ -19,7 +20,7 @@ class InstallCommand(MCPCommandBase):
19
20
  """
20
21
  Command handler for installing MCP servers from registry.
21
22
 
22
- Installs pre-configured MCP servers with optional interactive wizard.
23
+ Installs pre-configured MCP servers with interactive menu-based browser.
23
24
  """
24
25
 
25
26
  def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
@@ -34,25 +35,10 @@ class InstallCommand(MCPCommandBase):
34
35
  group_id = self.generate_group_id()
35
36
 
36
37
  try:
37
- # If in TUI mode, show message to use Ctrl+T
38
- if is_tui_mode():
39
- emit_info(
40
- "In TUI mode, use Ctrl+T to open the MCP Install Wizard",
41
- message_group=group_id,
42
- )
43
- return
44
-
45
- # In interactive mode, use the comprehensive installer
38
+ # In interactive mode, use the menu-based browser
46
39
  if not args:
47
- # No args - launch interactive wizard
48
- success = run_interactive_install_wizard(self.manager, group_id)
49
- if success:
50
- try:
51
- from code_puppy.agent import reload_mcp_servers
52
-
53
- reload_mcp_servers()
54
- except ImportError:
55
- pass
40
+ # No args - launch interactive menu
41
+ run_mcp_install_menu(self.manager)
56
42
  return
57
43
 
58
44
  # Has args - install directly from catalog
@@ -166,7 +152,9 @@ class InstallCommand(MCPCommandBase):
166
152
  required_env_vars = selected_server.get_environment_vars()
167
153
  if required_env_vars:
168
154
  emit_info(
169
- "\n[yellow]Required Environment Variables:[/yellow]",
155
+ Text.from_markup(
156
+ "\n[yellow]Required Environment Variables:[/yellow]"
157
+ ),
170
158
  message_group=group_id,
171
159
  )
172
160
  for var in required_env_vars:
@@ -176,7 +164,7 @@ class InstallCommand(MCPCommandBase):
176
164
  current_value = os.environ.get(var, "")
177
165
  if current_value:
178
166
  emit_info(
179
- f" {var}: [green]Already set[/green]",
167
+ Text.from_markup(f" {var}: [green]Already set[/green]"),
180
168
  message_group=group_id,
181
169
  )
182
170
  env_vars[var] = current_value
@@ -189,7 +177,8 @@ class InstallCommand(MCPCommandBase):
189
177
  required_cmd_args = selected_server.get_command_line_args()
190
178
  if required_cmd_args:
191
179
  emit_info(
192
- "\n[yellow]Command Line Arguments:[/yellow]", message_group=group_id
180
+ Text.from_markup("\n[yellow]Command Line Arguments:[/yellow]"),
181
+ message_group=group_id,
193
182
  )
194
183
  for arg_config in required_cmd_args:
195
184
  name = arg_config.get("name", "")
@@ -221,5 +210,5 @@ class InstallCommand(MCPCommandBase):
221
210
  return False
222
211
  except Exception as e:
223
212
  logger.error(f"Error installing from catalog: {e}")
224
- emit_info(f"[red]Installation error: {e}[/red]", message_group=group_id)
213
+ emit_error(f"Installation error: {e}", message_group=group_id)
225
214
  return False