code-puppy 0.0.134__py3-none-any.whl → 0.0.136__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/agent.py +15 -17
- code_puppy/agents/agent_manager.py +320 -9
- code_puppy/agents/base_agent.py +58 -2
- code_puppy/agents/runtime_manager.py +68 -42
- code_puppy/command_line/command_handler.py +82 -33
- code_puppy/command_line/mcp/__init__.py +10 -0
- code_puppy/command_line/mcp/add_command.py +183 -0
- code_puppy/command_line/mcp/base.py +35 -0
- code_puppy/command_line/mcp/handler.py +133 -0
- code_puppy/command_line/mcp/help_command.py +146 -0
- code_puppy/command_line/mcp/install_command.py +176 -0
- code_puppy/command_line/mcp/list_command.py +94 -0
- code_puppy/command_line/mcp/logs_command.py +126 -0
- code_puppy/command_line/mcp/remove_command.py +82 -0
- code_puppy/command_line/mcp/restart_command.py +92 -0
- code_puppy/command_line/mcp/search_command.py +117 -0
- code_puppy/command_line/mcp/start_all_command.py +126 -0
- code_puppy/command_line/mcp/start_command.py +98 -0
- code_puppy/command_line/mcp/status_command.py +185 -0
- code_puppy/command_line/mcp/stop_all_command.py +109 -0
- code_puppy/command_line/mcp/stop_command.py +79 -0
- code_puppy/command_line/mcp/test_command.py +107 -0
- code_puppy/command_line/mcp/utils.py +129 -0
- code_puppy/command_line/mcp/wizard_utils.py +259 -0
- code_puppy/command_line/model_picker_completion.py +21 -4
- code_puppy/command_line/prompt_toolkit_completion.py +9 -0
- code_puppy/main.py +23 -17
- code_puppy/mcp/__init__.py +42 -16
- code_puppy/mcp/async_lifecycle.py +51 -49
- code_puppy/mcp/blocking_startup.py +125 -113
- code_puppy/mcp/captured_stdio_server.py +63 -70
- code_puppy/mcp/circuit_breaker.py +63 -47
- code_puppy/mcp/config_wizard.py +169 -136
- code_puppy/mcp/dashboard.py +79 -71
- code_puppy/mcp/error_isolation.py +147 -100
- code_puppy/mcp/examples/retry_example.py +55 -42
- code_puppy/mcp/health_monitor.py +152 -141
- code_puppy/mcp/managed_server.py +100 -97
- code_puppy/mcp/manager.py +168 -156
- code_puppy/mcp/registry.py +148 -110
- code_puppy/mcp/retry_manager.py +63 -61
- code_puppy/mcp/server_registry_catalog.py +271 -225
- code_puppy/mcp/status_tracker.py +80 -80
- code_puppy/mcp/system_tools.py +47 -52
- code_puppy/messaging/message_queue.py +20 -13
- code_puppy/messaging/renderers.py +30 -15
- code_puppy/state_management.py +103 -0
- code_puppy/tui/app.py +64 -7
- code_puppy/tui/components/chat_view.py +3 -3
- code_puppy/tui/components/human_input_modal.py +12 -8
- code_puppy/tui/screens/__init__.py +2 -2
- code_puppy/tui/screens/mcp_install_wizard.py +208 -179
- code_puppy/tui/tests/test_agent_command.py +3 -3
- {code_puppy-0.0.134.dist-info → code_puppy-0.0.136.dist-info}/METADATA +1 -1
- {code_puppy-0.0.134.dist-info → code_puppy-0.0.136.dist-info}/RECORD +59 -41
- code_puppy/command_line/mcp_commands.py +0 -1789
- {code_puppy-0.0.134.data → code_puppy-0.0.136.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.134.dist-info → code_puppy-0.0.136.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.134.dist-info → code_puppy-0.0.136.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.134.dist-info → code_puppy-0.0.136.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Interactive Wizard Utilities - Shared interactive installation wizard functions.
|
|
3
|
+
|
|
4
|
+
Provides interactive functionality for installing and configuring MCP servers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from code_puppy.messaging import emit_info, emit_prompt
|
|
11
|
+
|
|
12
|
+
# Configure logging
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_interactive_install_wizard(manager, group_id: str) -> bool:
|
|
17
|
+
"""
|
|
18
|
+
Run the interactive MCP server installation wizard.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
manager: MCP manager instance
|
|
22
|
+
group_id: Message group ID for grouping related messages
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
True if installation was successful, False otherwise
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
# Show welcome message
|
|
29
|
+
emit_info("🚀 MCP Server Installation Wizard", message_group=group_id)
|
|
30
|
+
emit_info(
|
|
31
|
+
"This wizard will help you install pre-configured MCP servers",
|
|
32
|
+
message_group=group_id,
|
|
33
|
+
)
|
|
34
|
+
emit_info("", message_group=group_id)
|
|
35
|
+
|
|
36
|
+
# Let user select a server
|
|
37
|
+
selected_server = interactive_server_selection(group_id)
|
|
38
|
+
if not selected_server:
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
# Get custom name
|
|
42
|
+
server_name = interactive_get_server_name(selected_server, group_id)
|
|
43
|
+
if not server_name:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
# Configure the server
|
|
47
|
+
return interactive_configure_server(
|
|
48
|
+
manager, selected_server, server_name, group_id
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
except ImportError:
|
|
52
|
+
emit_info("[red]Server catalog not available[/red]", message_group=group_id)
|
|
53
|
+
return False
|
|
54
|
+
except Exception as e:
|
|
55
|
+
logger.error(f"Error in interactive wizard: {e}")
|
|
56
|
+
emit_info(f"[red]Wizard error: {e}[/red]", message_group=group_id)
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def interactive_server_selection(group_id: str):
|
|
61
|
+
"""
|
|
62
|
+
Interactive server selection from catalog.
|
|
63
|
+
|
|
64
|
+
Returns selected server or None if cancelled.
|
|
65
|
+
"""
|
|
66
|
+
# This is a simplified version - the full implementation would have
|
|
67
|
+
# category browsing, search, etc. For now, we'll just show popular servers
|
|
68
|
+
try:
|
|
69
|
+
from code_puppy.mcp.server_registry_catalog import catalog
|
|
70
|
+
|
|
71
|
+
servers = catalog.get_popular(10)
|
|
72
|
+
if not servers:
|
|
73
|
+
emit_info(
|
|
74
|
+
"[red]No servers available in catalog[/red]", message_group=group_id
|
|
75
|
+
)
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
emit_info("Popular MCP Servers:", message_group=group_id)
|
|
79
|
+
for i, server in enumerate(servers, 1):
|
|
80
|
+
indicators = []
|
|
81
|
+
if server.verified:
|
|
82
|
+
indicators.append("✓")
|
|
83
|
+
if server.popular:
|
|
84
|
+
indicators.append("⭐")
|
|
85
|
+
|
|
86
|
+
indicator_str = ""
|
|
87
|
+
if indicators:
|
|
88
|
+
indicator_str = " " + "".join(indicators)
|
|
89
|
+
|
|
90
|
+
emit_info(
|
|
91
|
+
f"{i:2}. {server.display_name}{indicator_str}", message_group=group_id
|
|
92
|
+
)
|
|
93
|
+
emit_info(f" {server.description[:80]}...", message_group=group_id)
|
|
94
|
+
|
|
95
|
+
choice = emit_prompt(
|
|
96
|
+
"Enter number (1-{}) or 'q' to quit: ".format(len(servers))
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if choice.lower() == "q":
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
index = int(choice) - 1
|
|
104
|
+
if 0 <= index < len(servers):
|
|
105
|
+
return servers[index]
|
|
106
|
+
else:
|
|
107
|
+
emit_info("[red]Invalid selection[/red]", message_group=group_id)
|
|
108
|
+
return None
|
|
109
|
+
except ValueError:
|
|
110
|
+
emit_info("[red]Invalid input[/red]", message_group=group_id)
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.error(f"Error in server selection: {e}")
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def interactive_get_server_name(selected_server, group_id: str) -> Optional[str]:
|
|
119
|
+
"""
|
|
120
|
+
Get custom server name from user.
|
|
121
|
+
|
|
122
|
+
Returns server name or None if cancelled.
|
|
123
|
+
"""
|
|
124
|
+
default_name = selected_server.name
|
|
125
|
+
server_name = emit_prompt(f"Enter name for this server [{default_name}]: ").strip()
|
|
126
|
+
|
|
127
|
+
if not server_name:
|
|
128
|
+
server_name = default_name
|
|
129
|
+
|
|
130
|
+
return server_name
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def interactive_configure_server(
|
|
134
|
+
manager, selected_server, server_name: str, group_id: str
|
|
135
|
+
) -> bool:
|
|
136
|
+
"""
|
|
137
|
+
Configure and install the selected server.
|
|
138
|
+
|
|
139
|
+
Returns True if successful, False otherwise.
|
|
140
|
+
"""
|
|
141
|
+
try:
|
|
142
|
+
# Check if server already exists
|
|
143
|
+
from .utils import find_server_id_by_name
|
|
144
|
+
|
|
145
|
+
existing_server = find_server_id_by_name(manager, server_name)
|
|
146
|
+
if existing_server:
|
|
147
|
+
override = emit_prompt(
|
|
148
|
+
f"Server '{server_name}' already exists. Override? [y/N]: "
|
|
149
|
+
)
|
|
150
|
+
if not override.lower().startswith("y"):
|
|
151
|
+
emit_info("Installation cancelled", message_group=group_id)
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
# For now, use defaults - a full implementation would collect env vars, etc.
|
|
155
|
+
# requirements = selected_server.get_requirements() # TODO: Use for validation
|
|
156
|
+
env_vars = {}
|
|
157
|
+
cmd_args = {}
|
|
158
|
+
|
|
159
|
+
# Show confirmation
|
|
160
|
+
emit_info(f"Installing: {selected_server.display_name}", message_group=group_id)
|
|
161
|
+
emit_info(f"Name: {server_name}", message_group=group_id)
|
|
162
|
+
|
|
163
|
+
confirm = emit_prompt("Proceed with installation? [Y/n]: ")
|
|
164
|
+
if confirm.lower().startswith("n"):
|
|
165
|
+
emit_info("Installation cancelled", message_group=group_id)
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
# Install the server (simplified version)
|
|
169
|
+
return install_server_from_catalog(
|
|
170
|
+
manager, selected_server, server_name, env_vars, cmd_args, group_id
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.error(f"Error configuring server: {e}")
|
|
175
|
+
emit_info(f"[red]Configuration error: {e}[/red]", message_group=group_id)
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def install_server_from_catalog(
|
|
180
|
+
manager,
|
|
181
|
+
selected_server,
|
|
182
|
+
server_name: str,
|
|
183
|
+
env_vars: Dict[str, Any],
|
|
184
|
+
cmd_args: Dict[str, Any],
|
|
185
|
+
group_id: str,
|
|
186
|
+
) -> bool:
|
|
187
|
+
"""
|
|
188
|
+
Install a server from the catalog with the given configuration.
|
|
189
|
+
|
|
190
|
+
Returns True if successful, False otherwise.
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
import json
|
|
194
|
+
import os
|
|
195
|
+
|
|
196
|
+
from code_puppy.config import MCP_SERVERS_FILE
|
|
197
|
+
from code_puppy.mcp.managed_server import ServerConfig
|
|
198
|
+
|
|
199
|
+
# Create server configuration
|
|
200
|
+
config_dict = selected_server.get_config_template()
|
|
201
|
+
|
|
202
|
+
# Apply environment variables and command args
|
|
203
|
+
if env_vars:
|
|
204
|
+
config_dict.update(env_vars)
|
|
205
|
+
if cmd_args:
|
|
206
|
+
config_dict.update(cmd_args)
|
|
207
|
+
|
|
208
|
+
# Create ServerConfig
|
|
209
|
+
server_config = ServerConfig(
|
|
210
|
+
id=f"{server_name}_{hash(server_name)}",
|
|
211
|
+
name=server_name,
|
|
212
|
+
type=selected_server.type,
|
|
213
|
+
enabled=True,
|
|
214
|
+
config=config_dict,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Register with manager
|
|
218
|
+
server_id = manager.register_server(server_config)
|
|
219
|
+
|
|
220
|
+
if not server_id:
|
|
221
|
+
emit_info(
|
|
222
|
+
"[red]Failed to register server with manager[/red]",
|
|
223
|
+
message_group=group_id,
|
|
224
|
+
)
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
# Save to mcp_servers.json for persistence
|
|
228
|
+
if os.path.exists(MCP_SERVERS_FILE):
|
|
229
|
+
with open(MCP_SERVERS_FILE, "r") as f:
|
|
230
|
+
data = json.load(f)
|
|
231
|
+
servers = data.get("mcp_servers", {})
|
|
232
|
+
else:
|
|
233
|
+
servers = {}
|
|
234
|
+
data = {"mcp_servers": servers}
|
|
235
|
+
|
|
236
|
+
# Add new server
|
|
237
|
+
servers[server_name] = config_dict.copy()
|
|
238
|
+
servers[server_name]["type"] = selected_server.type
|
|
239
|
+
|
|
240
|
+
# Save back
|
|
241
|
+
os.makedirs(os.path.dirname(MCP_SERVERS_FILE), exist_ok=True)
|
|
242
|
+
with open(MCP_SERVERS_FILE, "w") as f:
|
|
243
|
+
json.dump(data, f, indent=2)
|
|
244
|
+
|
|
245
|
+
emit_info(
|
|
246
|
+
f"[green]✓ Successfully installed server: {server_name}[/green]",
|
|
247
|
+
message_group=group_id,
|
|
248
|
+
)
|
|
249
|
+
emit_info(
|
|
250
|
+
"Use '/mcp start {}' to start the server".format(server_name),
|
|
251
|
+
message_group=group_id,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return True
|
|
255
|
+
|
|
256
|
+
except Exception as e:
|
|
257
|
+
logger.error(f"Error installing server: {e}")
|
|
258
|
+
emit_info(f"[red]Installation failed: {e}[/red]", message_group=group_id)
|
|
259
|
+
return False
|
|
@@ -70,9 +70,9 @@ class ModelNameCompleter(Completer):
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
def update_model_in_input(text: str) -> Optional[str]:
|
|
73
|
-
# If input starts with /model and a model name, set model and strip it out
|
|
73
|
+
# If input starts with /model or /m and a model name, set model and strip it out
|
|
74
74
|
content = text.strip()
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
# Check for /model command
|
|
77
77
|
if content.startswith("/model"):
|
|
78
78
|
rest = content[6:].strip() # Remove '/model'
|
|
@@ -82,13 +82,30 @@ def update_model_in_input(text: str) -> Optional[str]:
|
|
|
82
82
|
# Remove /model from the input
|
|
83
83
|
idx = text.find("/model" + model)
|
|
84
84
|
if idx != -1:
|
|
85
|
-
new_text = (
|
|
85
|
+
new_text = (
|
|
86
|
+
text[:idx] + text[idx + len("/model" + model) :]
|
|
87
|
+
).strip()
|
|
88
|
+
return new_text
|
|
89
|
+
|
|
90
|
+
# Check for /m command
|
|
91
|
+
elif content.startswith("/m "):
|
|
92
|
+
rest = content[3:].strip() # Remove '/m '
|
|
93
|
+
for model in load_model_names():
|
|
94
|
+
if rest == model:
|
|
95
|
+
set_active_model(model)
|
|
96
|
+
# Remove /m from the input
|
|
97
|
+
idx = text.find("/m " + model)
|
|
98
|
+
if idx != -1:
|
|
99
|
+
new_text = (text[:idx] + text[idx + len("/m " + model) :]).strip()
|
|
86
100
|
return new_text
|
|
101
|
+
|
|
87
102
|
return None
|
|
88
103
|
|
|
89
104
|
|
|
90
105
|
async def get_input_with_model_completion(
|
|
91
|
-
prompt_str: str = ">>> ",
|
|
106
|
+
prompt_str: str = ">>> ",
|
|
107
|
+
trigger: str = "/model",
|
|
108
|
+
history_file: Optional[str] = None,
|
|
92
109
|
) -> str:
|
|
93
110
|
history = FileHistory(os.path.expanduser(history_file)) if history_file else None
|
|
94
111
|
session = PromptSession(
|
|
@@ -136,8 +136,15 @@ class CDCompleter(Completer):
|
|
|
136
136
|
|
|
137
137
|
|
|
138
138
|
def get_prompt_with_active_model(base: str = ">>> "):
|
|
139
|
+
from code_puppy.agents.agent_manager import get_current_agent_config
|
|
140
|
+
|
|
139
141
|
puppy = get_puppy_name()
|
|
140
142
|
model = get_active_model() or "(default)"
|
|
143
|
+
|
|
144
|
+
# Get current agent information
|
|
145
|
+
current_agent = get_current_agent_config()
|
|
146
|
+
agent_display = current_agent.display_name if current_agent else "code-puppy"
|
|
147
|
+
|
|
141
148
|
cwd = os.getcwd()
|
|
142
149
|
home = os.path.expanduser("~")
|
|
143
150
|
if cwd.startswith(home):
|
|
@@ -149,6 +156,7 @@ def get_prompt_with_active_model(base: str = ">>> "):
|
|
|
149
156
|
("bold", "🐶 "),
|
|
150
157
|
("class:puppy", f"{puppy}"),
|
|
151
158
|
("", " "),
|
|
159
|
+
("class:agent", f"[{agent_display}] "),
|
|
152
160
|
("class:model", "[" + str(model) + "] "),
|
|
153
161
|
("class:cwd", "(" + str(cwd_display) + ") "),
|
|
154
162
|
("class:arrow", str(base)),
|
|
@@ -213,6 +221,7 @@ async def get_input_with_combined_completion(
|
|
|
213
221
|
# tagging tokens in `FormattedText`. See prompt_toolkit docs.
|
|
214
222
|
"puppy": "bold magenta",
|
|
215
223
|
"owner": "bold white",
|
|
224
|
+
"agent": "bold blue",
|
|
216
225
|
"model": "bold cyan",
|
|
217
226
|
"cwd": "bold green",
|
|
218
227
|
"arrow": "bold yellow",
|
code_puppy/main.py
CHANGED
|
@@ -11,7 +11,7 @@ from rich.markdown import CodeBlock, Markdown
|
|
|
11
11
|
from rich.syntax import Syntax
|
|
12
12
|
from rich.text import Text
|
|
13
13
|
|
|
14
|
-
from code_puppy import __version__, callbacks, plugins
|
|
14
|
+
from code_puppy import __version__, callbacks, plugins
|
|
15
15
|
from code_puppy.agent import get_custom_usage_limits
|
|
16
16
|
from code_puppy.agents.runtime_manager import get_runtime_agent_manager
|
|
17
17
|
from code_puppy.command_line.prompt_toolkit_completion import (
|
|
@@ -29,7 +29,7 @@ from code_puppy.message_history_processor import (
|
|
|
29
29
|
message_history_accumulator,
|
|
30
30
|
prune_interrupted_tool_calls,
|
|
31
31
|
)
|
|
32
|
-
from code_puppy.state_management import is_tui_mode,
|
|
32
|
+
from code_puppy.state_management import is_tui_mode, set_message_history, set_tui_mode
|
|
33
33
|
from code_puppy.tools.common import console
|
|
34
34
|
from code_puppy.version_checker import default_version_mismatch_behavior
|
|
35
35
|
|
|
@@ -263,8 +263,8 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
263
263
|
from code_puppy.messaging import emit_warning
|
|
264
264
|
|
|
265
265
|
emit_warning(f"MOTD error: {e}")
|
|
266
|
-
from code_puppy.messaging import emit_info
|
|
267
266
|
from code_puppy.agents.runtime_manager import get_runtime_agent_manager
|
|
267
|
+
from code_puppy.messaging import emit_info
|
|
268
268
|
|
|
269
269
|
emit_info("[bold cyan]Initializing agent...[/bold cyan]")
|
|
270
270
|
# Initialize the runtime agent manager
|
|
@@ -352,8 +352,8 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
352
352
|
emit_warning("Falling back to basic input without tab completion")
|
|
353
353
|
|
|
354
354
|
while True:
|
|
355
|
-
from code_puppy.messaging import emit_info
|
|
356
355
|
from code_puppy.agents.agent_manager import get_current_agent_config
|
|
356
|
+
from code_puppy.messaging import emit_info
|
|
357
357
|
|
|
358
358
|
# Get the custom prompt from the current agent, or use default
|
|
359
359
|
current_agent = get_current_agent_config()
|
|
@@ -426,15 +426,17 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
426
426
|
|
|
427
427
|
# Create a task that mimics TUI behavior - avoid signal handler conflicts
|
|
428
428
|
current_task = None
|
|
429
|
-
signal_handled =
|
|
430
|
-
|
|
429
|
+
signal_handled = (
|
|
430
|
+
False # Prevent multiple signal handler calls (reset per task)
|
|
431
|
+
)
|
|
432
|
+
|
|
431
433
|
async def run_task():
|
|
432
434
|
# Use the simpler run() method instead of run_with_mcp() to avoid signal handler
|
|
433
435
|
agent = agent_manager.get_agent()
|
|
434
436
|
async with agent:
|
|
435
437
|
return await agent.run(
|
|
436
438
|
task,
|
|
437
|
-
message_history=get_message_history(),
|
|
439
|
+
message_history=get_message_history(),
|
|
438
440
|
usage_limits=get_custom_usage_limits(),
|
|
439
441
|
)
|
|
440
442
|
|
|
@@ -444,27 +446,29 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
444
446
|
if signal_handled:
|
|
445
447
|
return
|
|
446
448
|
signal_handled = True
|
|
447
|
-
|
|
448
|
-
from code_puppy.tools.command_runner import
|
|
449
|
-
|
|
449
|
+
|
|
450
|
+
from code_puppy.tools.command_runner import (
|
|
451
|
+
kill_all_running_shell_processes,
|
|
452
|
+
)
|
|
453
|
+
|
|
450
454
|
killed = kill_all_running_shell_processes()
|
|
451
455
|
if killed:
|
|
452
456
|
emit_warning(f"🔥 Cancelled {killed} running shell process(es)")
|
|
453
457
|
# Don't cancel the agent task - let it continue processing
|
|
454
458
|
# Shell processes killed, but agent continues running
|
|
455
459
|
else:
|
|
456
|
-
# Only cancel the agent task if NO processes were killed
|
|
460
|
+
# Only cancel the agent task if NO processes were killed
|
|
457
461
|
if current_task and not current_task.done():
|
|
458
462
|
current_task.cancel()
|
|
459
463
|
emit_warning("⚠️ Processing cancelled by user")
|
|
460
464
|
|
|
461
465
|
# Set up proper signal handling to override asyncio's default behavior
|
|
462
466
|
import signal
|
|
463
|
-
|
|
467
|
+
|
|
464
468
|
def signal_handler(sig, frame):
|
|
465
469
|
"""Handle Ctrl+C by killing processes and cancelling the current task"""
|
|
466
470
|
handle_keyboard_interrupt()
|
|
467
|
-
|
|
471
|
+
|
|
468
472
|
# Replace asyncio's SIGINT handler with our own
|
|
469
473
|
original_handler = signal.signal(signal.SIGINT, signal_handler)
|
|
470
474
|
|
|
@@ -483,7 +487,7 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
483
487
|
result = None
|
|
484
488
|
finally:
|
|
485
489
|
# Restore original signal handler
|
|
486
|
-
if
|
|
490
|
+
if "original_handler" in locals():
|
|
487
491
|
signal.signal(signal.SIGINT, original_handler)
|
|
488
492
|
set_message_history(
|
|
489
493
|
prune_interrupted_tool_calls(get_message_history())
|
|
@@ -559,12 +563,12 @@ async def execute_single_prompt(prompt: str, message_renderer) -> None:
|
|
|
559
563
|
try:
|
|
560
564
|
# Get agent through runtime manager and use its run_with_mcp method
|
|
561
565
|
agent_manager = get_runtime_agent_manager()
|
|
562
|
-
|
|
566
|
+
|
|
563
567
|
from code_puppy.messaging.spinner import ConsoleSpinner
|
|
568
|
+
|
|
564
569
|
with ConsoleSpinner(console=message_renderer.console):
|
|
565
570
|
response = await agent_manager.run_with_mcp(
|
|
566
|
-
prompt,
|
|
567
|
-
usage_limits=get_custom_usage_limits()
|
|
571
|
+
prompt, usage_limits=get_custom_usage_limits()
|
|
568
572
|
)
|
|
569
573
|
|
|
570
574
|
agent_response = response.output
|
|
@@ -574,9 +578,11 @@ async def execute_single_prompt(prompt: str, message_renderer) -> None:
|
|
|
574
578
|
|
|
575
579
|
except asyncio.CancelledError:
|
|
576
580
|
from code_puppy.messaging import emit_warning
|
|
581
|
+
|
|
577
582
|
emit_warning("Execution cancelled by user")
|
|
578
583
|
except Exception as e:
|
|
579
584
|
from code_puppy.messaging import emit_error
|
|
585
|
+
|
|
580
586
|
emit_error(f"Error executing prompt: {str(e)}")
|
|
581
587
|
|
|
582
588
|
|
code_puppy/mcp/__init__.py
CHANGED
|
@@ -1,23 +1,49 @@
|
|
|
1
|
-
"""MCP (Model Context Protocol) management system for Code Puppy.
|
|
1
|
+
"""MCP (Model Context Protocol) management system for Code Puppy.
|
|
2
2
|
|
|
3
|
+
Note: Be careful not to create circular imports with config_wizard.py.
|
|
4
|
+
config_wizard.py imports ServerConfig and get_mcp_manager directly from
|
|
5
|
+
.manager to avoid circular dependencies with this package __init__.py
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .circuit_breaker import CircuitBreaker, CircuitOpenError, CircuitState
|
|
9
|
+
from .config_wizard import MCPConfigWizard, run_add_wizard
|
|
10
|
+
from .dashboard import MCPDashboard
|
|
11
|
+
from .error_isolation import (
|
|
12
|
+
ErrorCategory,
|
|
13
|
+
ErrorStats,
|
|
14
|
+
MCPErrorIsolator,
|
|
15
|
+
QuarantinedServerError,
|
|
16
|
+
get_error_isolator,
|
|
17
|
+
)
|
|
3
18
|
from .managed_server import ManagedMCPServer, ServerConfig, ServerState
|
|
4
|
-
from .status_tracker import ServerStatusTracker, Event
|
|
5
19
|
from .manager import MCPManager, ServerInfo, get_mcp_manager
|
|
6
20
|
from .registry import ServerRegistry
|
|
7
|
-
from .error_isolation import MCPErrorIsolator, ErrorStats, ErrorCategory, QuarantinedServerError, get_error_isolator
|
|
8
|
-
from .circuit_breaker import CircuitBreaker, CircuitState, CircuitOpenError
|
|
9
21
|
from .retry_manager import RetryManager, RetryStats, get_retry_manager, retry_mcp_call
|
|
10
|
-
from .
|
|
11
|
-
from .config_wizard import MCPConfigWizard, run_add_wizard
|
|
22
|
+
from .status_tracker import Event, ServerStatusTracker
|
|
12
23
|
|
|
13
24
|
__all__ = [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
"ManagedMCPServer",
|
|
26
|
+
"ServerConfig",
|
|
27
|
+
"ServerState",
|
|
28
|
+
"ServerStatusTracker",
|
|
29
|
+
"Event",
|
|
30
|
+
"MCPManager",
|
|
31
|
+
"ServerInfo",
|
|
32
|
+
"get_mcp_manager",
|
|
33
|
+
"ServerRegistry",
|
|
34
|
+
"MCPErrorIsolator",
|
|
35
|
+
"ErrorStats",
|
|
36
|
+
"ErrorCategory",
|
|
37
|
+
"QuarantinedServerError",
|
|
38
|
+
"get_error_isolator",
|
|
39
|
+
"CircuitBreaker",
|
|
40
|
+
"CircuitState",
|
|
41
|
+
"CircuitOpenError",
|
|
42
|
+
"RetryManager",
|
|
43
|
+
"RetryStats",
|
|
44
|
+
"get_retry_manager",
|
|
45
|
+
"retry_mcp_call",
|
|
46
|
+
"MCPDashboard",
|
|
47
|
+
"MCPConfigWizard",
|
|
48
|
+
"run_add_wizard",
|
|
49
|
+
]
|