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.
- code_puppy/__init__.py +7 -1
- code_puppy/agents/__init__.py +2 -0
- code_puppy/agents/agent_c_reviewer.py +59 -6
- code_puppy/agents/agent_code_puppy.py +7 -1
- code_puppy/agents/agent_code_reviewer.py +12 -2
- code_puppy/agents/agent_cpp_reviewer.py +73 -6
- code_puppy/agents/agent_creator_agent.py +45 -4
- code_puppy/agents/agent_golang_reviewer.py +92 -3
- code_puppy/agents/agent_javascript_reviewer.py +101 -8
- code_puppy/agents/agent_manager.py +81 -4
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_planning.py +163 -0
- code_puppy/agents/agent_python_programmer.py +165 -0
- code_puppy/agents/agent_python_reviewer.py +28 -6
- code_puppy/agents/agent_qa_expert.py +98 -6
- code_puppy/agents/agent_qa_kitten.py +12 -7
- code_puppy/agents/agent_security_auditor.py +113 -3
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +106 -7
- code_puppy/agents/base_agent.py +802 -176
- code_puppy/agents/event_stream_handler.py +350 -0
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +321 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +446 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +74 -0
- code_puppy/api/routers/sessions.py +232 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +142 -4
- code_puppy/chatgpt_codex_client.py +283 -0
- code_puppy/claude_cache_client.py +586 -0
- code_puppy/cli_runner.py +916 -0
- code_puppy/command_line/add_model_menu.py +1079 -0
- code_puppy/command_line/agent_menu.py +395 -0
- code_puppy/command_line/attachments.py +10 -5
- code_puppy/command_line/autosave_menu.py +605 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +520 -0
- code_puppy/command_line/command_handler.py +176 -738
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +715 -0
- code_puppy/command_line/core_commands.py +792 -0
- code_puppy/command_line/diff_menu.py +863 -0
- code_puppy/command_line/load_context_completion.py +15 -22
- code_puppy/command_line/mcp/base.py +0 -3
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +9 -4
- code_puppy/command_line/mcp/help_command.py +6 -5
- code_puppy/command_line/mcp/install_command.py +15 -26
- code_puppy/command_line/mcp/install_menu.py +685 -0
- code_puppy/command_line/mcp/list_command.py +2 -2
- code_puppy/command_line/mcp/logs_command.py +174 -65
- code_puppy/command_line/mcp/remove_command.py +2 -2
- code_puppy/command_line/mcp/restart_command.py +12 -4
- code_puppy/command_line/mcp/search_command.py +16 -10
- code_puppy/command_line/mcp/start_all_command.py +18 -6
- code_puppy/command_line/mcp/start_command.py +47 -25
- code_puppy/command_line/mcp/status_command.py +4 -5
- code_puppy/command_line/mcp/stop_all_command.py +7 -1
- code_puppy/command_line/mcp/stop_command.py +8 -4
- code_puppy/command_line/mcp/test_command.py +2 -2
- code_puppy/command_line/mcp/wizard_utils.py +20 -16
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +75 -25
- code_puppy/command_line/model_settings_menu.py +884 -0
- code_puppy/command_line/motd.py +14 -8
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +340 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +463 -63
- code_puppy/command_line/session_commands.py +296 -0
- code_puppy/command_line/utils.py +54 -0
- code_puppy/config.py +898 -112
- code_puppy/error_logging.py +118 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +602 -0
- code_puppy/http_utils.py +210 -148
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +5 -698
- code_puppy/mcp_/__init__.py +17 -0
- code_puppy/mcp_/async_lifecycle.py +35 -4
- code_puppy/mcp_/blocking_startup.py +70 -43
- code_puppy/mcp_/captured_stdio_server.py +2 -2
- code_puppy/mcp_/config_wizard.py +4 -4
- code_puppy/mcp_/dashboard.py +15 -6
- code_puppy/mcp_/managed_server.py +65 -38
- code_puppy/mcp_/manager.py +146 -52
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +6 -6
- code_puppy/mcp_/server_registry_catalog.py +24 -5
- code_puppy/messaging/__init__.py +199 -2
- code_puppy/messaging/bus.py +610 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +17 -48
- code_puppy/messaging/messages.py +500 -0
- code_puppy/messaging/queue_console.py +1 -24
- code_puppy/messaging/renderers.py +43 -146
- code_puppy/messaging/rich_renderer.py +1027 -0
- code_puppy/messaging/spinner/__init__.py +21 -5
- code_puppy/messaging/spinner/console_spinner.py +86 -51
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_factory.py +634 -83
- code_puppy/model_utils.py +167 -0
- code_puppy/models.json +66 -68
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +164 -10
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +704 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +136 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
- code_puppy/plugins/antigravity_oauth/storage.py +271 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +767 -0
- code_puppy/plugins/antigravity_oauth/utils.py +169 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +94 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +293 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +489 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +6 -0
- code_puppy/plugins/claude_code_oauth/config.py +50 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +308 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/utils.py +518 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +169 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +2 -2
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +523 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/prompts/codex_system_prompt.md +310 -0
- code_puppy/pydantic_patches.py +131 -0
- code_puppy/reopenable_async_client.py +8 -8
- code_puppy/round_robin_model.py +9 -12
- code_puppy/session_storage.py +2 -1
- code_puppy/status_display.py +21 -4
- code_puppy/summarization_agent.py +41 -13
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +37 -1
- code_puppy/tools/agent_tools.py +536 -52
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +19 -23
- code_puppy/tools/browser/browser_interactions.py +41 -48
- code_puppy/tools/browser/browser_locators.py +36 -38
- code_puppy/tools/browser/browser_manager.py +316 -0
- code_puppy/tools/browser/browser_navigation.py +16 -16
- code_puppy/tools/browser/browser_screenshot.py +79 -143
- code_puppy/tools/browser/browser_scripts.py +32 -42
- code_puppy/tools/browser/browser_workflows.py +44 -27
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +521 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +930 -147
- code_puppy/tools/common.py +1113 -5
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +288 -89
- code_puppy/tools/file_operations.py +226 -154
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +30 -11
- code_puppy-0.0.366.data/data/code_puppy/models.json +110 -0
- code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json +1 -0
- {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/METADATA +149 -75
- code_puppy-0.0.366.dist-info/RECORD +217 -0
- {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/WHEEL +1 -1
- code_puppy/command_line/mcp/add_command.py +0 -183
- code_puppy/messaging/spinner/textual_spinner.py +0 -106
- code_puppy/tools/browser/camoufox_manager.py +0 -216
- code_puppy/tools/browser/vqa_agent.py +0 -70
- code_puppy/tui/__init__.py +0 -10
- code_puppy/tui/app.py +0 -1105
- code_puppy/tui/components/__init__.py +0 -21
- code_puppy/tui/components/chat_view.py +0 -551
- code_puppy/tui/components/command_history_modal.py +0 -218
- code_puppy/tui/components/copy_button.py +0 -139
- code_puppy/tui/components/custom_widgets.py +0 -63
- code_puppy/tui/components/human_input_modal.py +0 -175
- code_puppy/tui/components/input_area.py +0 -167
- code_puppy/tui/components/sidebar.py +0 -309
- code_puppy/tui/components/status_bar.py +0 -185
- code_puppy/tui/messages.py +0 -27
- code_puppy/tui/models/__init__.py +0 -8
- code_puppy/tui/models/chat_message.py +0 -25
- code_puppy/tui/models/command_history.py +0 -89
- code_puppy/tui/models/enums.py +0 -24
- code_puppy/tui/screens/__init__.py +0 -17
- code_puppy/tui/screens/autosave_picker.py +0 -175
- code_puppy/tui/screens/help.py +0 -130
- code_puppy/tui/screens/mcp_install_wizard.py +0 -803
- code_puppy/tui/screens/settings.py +0 -306
- code_puppy/tui/screens/tools.py +0 -74
- code_puppy/tui_state.py +0 -55
- code_puppy-0.0.214.data/data/code_puppy/models.json +0 -112
- code_puppy-0.0.214.dist-info/RECORD +0 -131
- {code_puppy-0.0.214.dist-info → code_puppy-0.0.366.dist-info}/entry_points.txt +0 -0
- {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
|
-
"
|
|
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]",
|
|
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
|
-
|
|
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
|
|
106
|
-
+ Text("
|
|
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
|
|
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
|
-
|
|
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
|
|
9
|
-
|
|
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 .
|
|
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
|
|
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
|
-
#
|
|
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
|
|
48
|
-
|
|
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
|
-
|
|
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]",
|
|
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
|
-
|
|
213
|
+
emit_error(f"Installation error: {e}", message_group=group_id)
|
|
225
214
|
return False
|