code-puppy 0.0.302__py3-none-any.whl → 0.0.335__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/agents/base_agent.py +343 -35
- code_puppy/chatgpt_codex_client.py +283 -0
- code_puppy/cli_runner.py +898 -0
- code_puppy/command_line/add_model_menu.py +23 -1
- code_puppy/command_line/autosave_menu.py +271 -35
- code_puppy/command_line/colors_menu.py +520 -0
- code_puppy/command_line/command_handler.py +8 -2
- code_puppy/command_line/config_commands.py +82 -10
- code_puppy/command_line/core_commands.py +70 -7
- code_puppy/command_line/diff_menu.py +5 -0
- code_puppy/command_line/mcp/custom_server_form.py +4 -0
- code_puppy/command_line/mcp/edit_command.py +3 -1
- code_puppy/command_line/mcp/handler.py +7 -2
- code_puppy/command_line/mcp/install_command.py +8 -3
- code_puppy/command_line/mcp/install_menu.py +5 -1
- code_puppy/command_line/mcp/logs_command.py +173 -64
- code_puppy/command_line/mcp/restart_command.py +7 -2
- code_puppy/command_line/mcp/search_command.py +10 -4
- code_puppy/command_line/mcp/start_all_command.py +16 -6
- code_puppy/command_line/mcp/start_command.py +3 -1
- code_puppy/command_line/mcp/status_command.py +2 -1
- code_puppy/command_line/mcp/stop_all_command.py +5 -1
- code_puppy/command_line/mcp/stop_command.py +3 -1
- code_puppy/command_line/mcp/wizard_utils.py +10 -4
- code_puppy/command_line/model_settings_menu.py +58 -7
- code_puppy/command_line/motd.py +13 -7
- code_puppy/command_line/onboarding_slides.py +180 -0
- code_puppy/command_line/onboarding_wizard.py +340 -0
- code_puppy/command_line/prompt_toolkit_completion.py +16 -2
- code_puppy/command_line/session_commands.py +11 -4
- code_puppy/config.py +106 -17
- code_puppy/http_utils.py +155 -196
- code_puppy/keymap.py +8 -0
- code_puppy/main.py +5 -828
- code_puppy/mcp_/__init__.py +17 -0
- code_puppy/mcp_/blocking_startup.py +61 -32
- code_puppy/mcp_/config_wizard.py +5 -1
- code_puppy/mcp_/managed_server.py +23 -3
- code_puppy/mcp_/manager.py +65 -0
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/messaging/__init__.py +20 -4
- code_puppy/messaging/bus.py +64 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/messages.py +16 -0
- code_puppy/messaging/renderers.py +21 -9
- code_puppy/messaging/rich_renderer.py +113 -67
- code_puppy/messaging/spinner/console_spinner.py +34 -0
- code_puppy/model_factory.py +271 -45
- code_puppy/model_utils.py +57 -48
- code_puppy/models.json +21 -7
- code_puppy/plugins/__init__.py +12 -0
- 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 +612 -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 +595 -0
- code_puppy/plugins/antigravity_oauth/utils.py +169 -0
- code_puppy/plugins/chatgpt_oauth/config.py +5 -1
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +5 -6
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +5 -3
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +26 -11
- code_puppy/plugins/chatgpt_oauth/utils.py +180 -65
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +30 -0
- code_puppy/plugins/claude_code_oauth/utils.py +1 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +1 -118
- code_puppy/plugins/shell_safety/register_callbacks.py +44 -3
- 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/terminal_utils.py +291 -0
- code_puppy/tools/agent_tools.py +34 -9
- code_puppy/tools/command_runner.py +344 -27
- code_puppy/tools/file_operations.py +33 -45
- code_puppy/uvx_detection.py +242 -0
- {code_puppy-0.0.302.data → code_puppy-0.0.335.data}/data/code_puppy/models.json +21 -7
- {code_puppy-0.0.302.dist-info → code_puppy-0.0.335.dist-info}/METADATA +30 -1
- {code_puppy-0.0.302.dist-info → code_puppy-0.0.335.dist-info}/RECORD +87 -64
- {code_puppy-0.0.302.data → code_puppy-0.0.335.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.302.dist-info → code_puppy-0.0.335.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.302.dist-info → code_puppy-0.0.335.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.302.dist-info → code_puppy-0.0.335.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
"""
|
|
2
|
-
MCP Logs Command - Shows
|
|
2
|
+
MCP Logs Command - Shows server logs from persistent log files.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
-
from datetime import datetime
|
|
7
6
|
from typing import List, Optional
|
|
8
7
|
|
|
9
|
-
from rich.
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.syntax import Syntax
|
|
10
10
|
from rich.text import Text
|
|
11
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
|
+
)
|
|
12
19
|
from code_puppy.messaging import emit_error, emit_info
|
|
13
20
|
|
|
14
21
|
from .base import MCPCommandBase
|
|
@@ -22,105 +29,207 @@ class LogsCommand(MCPCommandBase):
|
|
|
22
29
|
"""
|
|
23
30
|
Command handler for showing MCP server logs.
|
|
24
31
|
|
|
25
|
-
Shows
|
|
32
|
+
Shows logs from persistent log files stored in ~/.code_puppy/mcp_logs/.
|
|
26
33
|
"""
|
|
27
34
|
|
|
28
35
|
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
29
36
|
"""
|
|
30
|
-
Show
|
|
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
|
|
31
45
|
|
|
32
46
|
Args:
|
|
33
|
-
args: Command arguments
|
|
47
|
+
args: Command arguments
|
|
34
48
|
group_id: Optional message group ID for grouping related messages
|
|
35
49
|
"""
|
|
36
50
|
if group_id is None:
|
|
37
51
|
group_id = self.generate_group_id()
|
|
38
52
|
|
|
53
|
+
# No args - list servers with logs
|
|
39
54
|
if not args:
|
|
40
|
-
|
|
55
|
+
self._list_servers_with_logs(group_id)
|
|
41
56
|
return
|
|
42
57
|
|
|
43
58
|
server_name = args[0]
|
|
44
|
-
|
|
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
|
|
45
68
|
|
|
46
69
|
if len(args) > 1:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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:
|
|
50
82
|
emit_info(
|
|
51
|
-
"
|
|
83
|
+
f"Invalid number '{args[1]}', using default: 50",
|
|
52
84
|
message_group=group_id,
|
|
53
85
|
)
|
|
54
|
-
limit = 10
|
|
55
|
-
except ValueError:
|
|
56
|
-
emit_info(
|
|
57
|
-
f"Invalid limit '{args[1]}', using default: 10",
|
|
58
|
-
message_group=group_id,
|
|
59
|
-
)
|
|
60
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
|
+
"""
|
|
61
131
|
try:
|
|
62
|
-
#
|
|
132
|
+
# Verify server exists in manager
|
|
63
133
|
server_id = find_server_id_by_name(self.manager, server_name)
|
|
64
134
|
if not server_id:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
68
146
|
|
|
69
|
-
#
|
|
70
|
-
|
|
147
|
+
# Read logs
|
|
148
|
+
log_lines = read_logs(server_name, lines=lines)
|
|
71
149
|
|
|
72
|
-
if not
|
|
150
|
+
if not log_lines:
|
|
73
151
|
emit_info(
|
|
74
|
-
f"
|
|
152
|
+
f"📋 No logs found for server: **{server_name}**\n"
|
|
153
|
+
f"Log file: `{get_log_file_path(server_name)}`",
|
|
75
154
|
message_group=group_id,
|
|
76
155
|
)
|
|
77
156
|
return
|
|
78
157
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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)"
|
|
85
169
|
)
|
|
86
|
-
return
|
|
87
170
|
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
+
)
|
|
93
182
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
183
|
+
panel = Panel(
|
|
184
|
+
syntax,
|
|
185
|
+
title=header,
|
|
186
|
+
subtitle=f"Log file: {get_log_file_path(server_name)}",
|
|
187
|
+
border_style="dim",
|
|
97
188
|
)
|
|
98
189
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# Color code event types
|
|
111
|
-
event_style = "cyan"
|
|
112
|
-
if "error" in event_type.lower():
|
|
113
|
-
event_style = "red"
|
|
114
|
-
elif event_type in ["started", "enabled", "registered"]:
|
|
115
|
-
event_style = "green"
|
|
116
|
-
elif event_type in ["stopped", "disabled"]:
|
|
117
|
-
event_style = "yellow"
|
|
118
|
-
|
|
119
|
-
table.add_row(
|
|
120
|
-
time_str, Text(event_type, style=event_style), details_str or "-"
|
|
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,
|
|
121
200
|
)
|
|
122
|
-
emit_info(table, message_group=group_id)
|
|
123
201
|
|
|
124
202
|
except Exception as e:
|
|
125
203
|
logger.error(f"Error getting logs for server '{server_name}': {e}")
|
|
126
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)
|
|
@@ -5,6 +5,8 @@ MCP Restart Command - Restarts a specific MCP server.
|
|
|
5
5
|
import logging
|
|
6
6
|
from typing import List, Optional
|
|
7
7
|
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
8
10
|
from code_puppy.messaging import emit_info
|
|
9
11
|
|
|
10
12
|
from .base import MCPCommandBase
|
|
@@ -72,7 +74,9 @@ class RestartCommand(MCPCommandBase):
|
|
|
72
74
|
# Update MCP tool cache immediately so token counts reflect the change
|
|
73
75
|
agent.update_mcp_tool_cache_sync()
|
|
74
76
|
emit_info(
|
|
75
|
-
|
|
77
|
+
Text.from_markup(
|
|
78
|
+
"[dim]Agent reloaded with updated servers[/dim]"
|
|
79
|
+
),
|
|
76
80
|
message_group=group_id,
|
|
77
81
|
)
|
|
78
82
|
except Exception as e:
|
|
@@ -91,5 +95,6 @@ class RestartCommand(MCPCommandBase):
|
|
|
91
95
|
except Exception as e:
|
|
92
96
|
logger.error(f"Error restarting server '{server_name}': {e}")
|
|
93
97
|
emit_info(
|
|
94
|
-
f"[red]Failed to restart server: {e}[/red]",
|
|
98
|
+
Text.from_markup(f"[red]Failed to restart server: {e}[/red]"),
|
|
99
|
+
message_group=group_id,
|
|
95
100
|
)
|
|
@@ -6,6 +6,7 @@ import logging
|
|
|
6
6
|
from typing import List, Optional
|
|
7
7
|
|
|
8
8
|
from rich.table import Table
|
|
9
|
+
from rich.text import Text
|
|
9
10
|
|
|
10
11
|
from code_puppy.messaging import emit_info, emit_system_message, emit_warning
|
|
11
12
|
|
|
@@ -99,19 +100,24 @@ class SearchCommand(MCPCommandBase):
|
|
|
99
100
|
emit_system_message(table, message_group=group_id)
|
|
100
101
|
emit_info("\n✓ = Verified ⭐ = Popular", message_group=group_id)
|
|
101
102
|
emit_info(
|
|
102
|
-
"[yellow]To install:[/yellow] /mcp install <id>",
|
|
103
|
+
Text.from_markup("[yellow]To install:[/yellow] /mcp install <id>"),
|
|
104
|
+
message_group=group_id,
|
|
103
105
|
)
|
|
104
106
|
emit_info(
|
|
105
|
-
|
|
107
|
+
Text.from_markup(
|
|
108
|
+
"[yellow]For details:[/yellow] /mcp search <specific-term>"
|
|
109
|
+
),
|
|
106
110
|
message_group=group_id,
|
|
107
111
|
)
|
|
108
112
|
|
|
109
113
|
except ImportError:
|
|
110
114
|
emit_info(
|
|
111
|
-
"[red]Server registry not available[/red]",
|
|
115
|
+
Text.from_markup("[red]Server registry not available[/red]"),
|
|
116
|
+
message_group=group_id,
|
|
112
117
|
)
|
|
113
118
|
except Exception as e:
|
|
114
119
|
logger.error(f"Error searching server registry: {e}")
|
|
115
120
|
emit_info(
|
|
116
|
-
f"[red]Error searching servers: {e}[/red]",
|
|
121
|
+
Text.from_markup(f"[red]Error searching servers: {e}[/red]"),
|
|
122
|
+
message_group=group_id,
|
|
117
123
|
)
|
|
@@ -6,6 +6,8 @@ import logging
|
|
|
6
6
|
import time
|
|
7
7
|
from typing import List, Optional
|
|
8
8
|
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
9
11
|
from code_puppy.mcp_.managed_server import ServerState
|
|
10
12
|
from code_puppy.messaging import emit_info
|
|
11
13
|
|
|
@@ -67,20 +69,23 @@ class StartAllCommand(MCPCommandBase):
|
|
|
67
69
|
if success:
|
|
68
70
|
started_count += 1
|
|
69
71
|
emit_info(
|
|
70
|
-
f" [green]✓ Started: {server_name}[/green]",
|
|
72
|
+
Text.from_markup(f" [green]✓ Started: {server_name}[/green]"),
|
|
71
73
|
message_group=group_id,
|
|
72
74
|
)
|
|
73
75
|
else:
|
|
74
76
|
failed_count += 1
|
|
75
77
|
emit_info(
|
|
76
|
-
f" [red]✗ Failed: {server_name}[/red]",
|
|
78
|
+
Text.from_markup(f" [red]✗ Failed: {server_name}[/red]"),
|
|
79
|
+
message_group=group_id,
|
|
77
80
|
)
|
|
78
81
|
|
|
79
82
|
# Summary
|
|
80
83
|
emit_info("", message_group=group_id)
|
|
81
84
|
if started_count > 0:
|
|
82
85
|
emit_info(
|
|
83
|
-
|
|
86
|
+
Text.from_markup(
|
|
87
|
+
f"[green]Started {started_count} server(s)[/green]"
|
|
88
|
+
),
|
|
84
89
|
message_group=group_id,
|
|
85
90
|
)
|
|
86
91
|
if already_running > 0:
|
|
@@ -90,7 +95,9 @@ class StartAllCommand(MCPCommandBase):
|
|
|
90
95
|
)
|
|
91
96
|
if failed_count > 0:
|
|
92
97
|
emit_info(
|
|
93
|
-
|
|
98
|
+
Text.from_markup(
|
|
99
|
+
f"[yellow]Failed to start {failed_count} server(s)[/yellow]"
|
|
100
|
+
),
|
|
94
101
|
message_group=group_id,
|
|
95
102
|
)
|
|
96
103
|
|
|
@@ -112,7 +119,9 @@ class StartAllCommand(MCPCommandBase):
|
|
|
112
119
|
# Update MCP tool cache immediately so token counts reflect the change
|
|
113
120
|
agent.update_mcp_tool_cache_sync()
|
|
114
121
|
emit_info(
|
|
115
|
-
|
|
122
|
+
Text.from_markup(
|
|
123
|
+
"[dim]Agent reloaded with updated servers[/dim]"
|
|
124
|
+
),
|
|
116
125
|
message_group=group_id,
|
|
117
126
|
)
|
|
118
127
|
except Exception as e:
|
|
@@ -121,5 +130,6 @@ class StartAllCommand(MCPCommandBase):
|
|
|
121
130
|
except Exception as e:
|
|
122
131
|
logger.error(f"Error starting all servers: {e}")
|
|
123
132
|
emit_info(
|
|
124
|
-
f"[red]Failed to start servers: {e}[/red]",
|
|
133
|
+
Text.from_markup(f"[red]Failed to start servers: {e}[/red]"),
|
|
134
|
+
message_group=group_id,
|
|
125
135
|
)
|
|
@@ -6,6 +6,8 @@ import logging
|
|
|
6
6
|
import time
|
|
7
7
|
from typing import List, Optional
|
|
8
8
|
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
9
11
|
from code_puppy.messaging import emit_error, emit_info, emit_success
|
|
10
12
|
|
|
11
13
|
from ...agents import get_current_agent
|
|
@@ -36,7 +38,7 @@ class StartCommand(MCPCommandBase):
|
|
|
36
38
|
|
|
37
39
|
if not args:
|
|
38
40
|
emit_info(
|
|
39
|
-
"[yellow]Usage: /mcp start <server_name>[/yellow]",
|
|
41
|
+
Text.from_markup("[yellow]Usage: /mcp start <server_name>[/yellow]"),
|
|
40
42
|
message_group=group_id,
|
|
41
43
|
)
|
|
42
44
|
return
|
|
@@ -7,6 +7,7 @@ from datetime import datetime
|
|
|
7
7
|
from typing import List, Optional
|
|
8
8
|
|
|
9
9
|
from rich.panel import Panel
|
|
10
|
+
from rich.text import Text
|
|
10
11
|
|
|
11
12
|
from code_puppy.mcp_.managed_server import ServerState
|
|
12
13
|
from code_puppy.messaging import emit_error, emit_info
|
|
@@ -158,7 +159,7 @@ class StatusCommand(MCPCommandBase):
|
|
|
158
159
|
status_lines.append(f"[bold]Metadata:[/bold] {len(metadata)} keys")
|
|
159
160
|
|
|
160
161
|
# Create and show the panel
|
|
161
|
-
panel_content = "\n".join(status_lines)
|
|
162
|
+
panel_content = Text.from_markup("\n".join(status_lines))
|
|
162
163
|
panel = Panel(
|
|
163
164
|
panel_content, title=f"🔌 {server_name} Status", border_style="cyan"
|
|
164
165
|
)
|
|
@@ -6,6 +6,8 @@ import logging
|
|
|
6
6
|
import time
|
|
7
7
|
from typing import List, Optional
|
|
8
8
|
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
9
11
|
from code_puppy.mcp_.managed_server import ServerState
|
|
10
12
|
from code_puppy.messaging import emit_info
|
|
11
13
|
|
|
@@ -97,7 +99,9 @@ class StopAllCommand(MCPCommandBase):
|
|
|
97
99
|
# Update MCP tool cache immediately so token counts reflect the change
|
|
98
100
|
agent.update_mcp_tool_cache_sync()
|
|
99
101
|
emit_info(
|
|
100
|
-
|
|
102
|
+
Text.from_markup(
|
|
103
|
+
"[dim]Agent reloaded with updated servers[/dim]"
|
|
104
|
+
),
|
|
101
105
|
message_group=group_id,
|
|
102
106
|
)
|
|
103
107
|
except Exception as e:
|
|
@@ -5,6 +5,8 @@ MCP Stop Command - Stops a specific MCP server.
|
|
|
5
5
|
import logging
|
|
6
6
|
from typing import List, Optional
|
|
7
7
|
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
8
10
|
from code_puppy.messaging import emit_error, emit_info
|
|
9
11
|
|
|
10
12
|
from ...agents import get_current_agent
|
|
@@ -35,7 +37,7 @@ class StopCommand(MCPCommandBase):
|
|
|
35
37
|
|
|
36
38
|
if not args:
|
|
37
39
|
emit_info(
|
|
38
|
-
"[yellow]Usage: /mcp stop <server_name>[/yellow]",
|
|
40
|
+
Text.from_markup("[yellow]Usage: /mcp stop <server_name>[/yellow]"),
|
|
39
41
|
message_group=group_id,
|
|
40
42
|
)
|
|
41
43
|
return
|
|
@@ -7,6 +7,8 @@ Provides interactive functionality for installing and configuring MCP servers.
|
|
|
7
7
|
import logging
|
|
8
8
|
from typing import Any, Dict, Optional
|
|
9
9
|
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
|
|
10
12
|
from code_puppy.messaging import emit_error, emit_info, emit_prompt
|
|
11
13
|
|
|
12
14
|
# Configure logging
|
|
@@ -51,7 +53,7 @@ def run_interactive_install_wizard(manager, group_id: str) -> bool:
|
|
|
51
53
|
required_env_vars = selected_server.get_environment_vars()
|
|
52
54
|
if required_env_vars:
|
|
53
55
|
emit_info(
|
|
54
|
-
"\n[yellow]Required Environment Variables:[/yellow]",
|
|
56
|
+
Text.from_markup("\n[yellow]Required Environment Variables:[/yellow]"),
|
|
55
57
|
message_group=group_id,
|
|
56
58
|
)
|
|
57
59
|
for var in required_env_vars:
|
|
@@ -61,7 +63,8 @@ def run_interactive_install_wizard(manager, group_id: str) -> bool:
|
|
|
61
63
|
current_value = os.environ.get(var, "")
|
|
62
64
|
if current_value:
|
|
63
65
|
emit_info(
|
|
64
|
-
f" {var}: [green]Already set[/green]",
|
|
66
|
+
Text.from_markup(f" {var}: [green]Already set[/green]"),
|
|
67
|
+
message_group=group_id,
|
|
65
68
|
)
|
|
66
69
|
env_vars[var] = current_value
|
|
67
70
|
else:
|
|
@@ -73,7 +76,8 @@ def run_interactive_install_wizard(manager, group_id: str) -> bool:
|
|
|
73
76
|
required_cmd_args = selected_server.get_command_line_args()
|
|
74
77
|
if required_cmd_args:
|
|
75
78
|
emit_info(
|
|
76
|
-
"\n[yellow]Command Line Arguments:[/yellow]",
|
|
79
|
+
Text.from_markup("\n[yellow]Command Line Arguments:[/yellow]"),
|
|
80
|
+
message_group=group_id,
|
|
77
81
|
)
|
|
78
82
|
for arg_config in required_cmd_args:
|
|
79
83
|
name = arg_config.get("name", "")
|
|
@@ -312,7 +316,9 @@ def install_server_from_catalog(
|
|
|
312
316
|
json.dump(data, f, indent=2)
|
|
313
317
|
|
|
314
318
|
emit_info(
|
|
315
|
-
|
|
319
|
+
Text.from_markup(
|
|
320
|
+
f"[green]✓ Successfully installed server: {server_name}[/green]"
|
|
321
|
+
),
|
|
316
322
|
message_group=group_id,
|
|
317
323
|
)
|
|
318
324
|
emit_info(
|
|
@@ -58,7 +58,7 @@ SETTING_DEFINITIONS: Dict[str, Dict] = {
|
|
|
58
58
|
"name": "Reasoning Effort",
|
|
59
59
|
"description": "Controls how much effort GPT-5 models spend on reasoning. Higher = more thorough but slower.",
|
|
60
60
|
"type": "choice",
|
|
61
|
-
"choices": ["low", "medium", "high"],
|
|
61
|
+
"choices": ["minimal", "low", "medium", "high", "xhigh"],
|
|
62
62
|
"default": "medium",
|
|
63
63
|
},
|
|
64
64
|
"verbosity": {
|
|
@@ -72,7 +72,7 @@ SETTING_DEFINITIONS: Dict[str, Dict] = {
|
|
|
72
72
|
"name": "Extended Thinking",
|
|
73
73
|
"description": "Enable Claude's extended thinking mode for complex reasoning tasks.",
|
|
74
74
|
"type": "boolean",
|
|
75
|
-
"default":
|
|
75
|
+
"default": True,
|
|
76
76
|
},
|
|
77
77
|
"budget_tokens": {
|
|
78
78
|
"name": "Thinking Budget (tokens)",
|
|
@@ -84,6 +84,12 @@ SETTING_DEFINITIONS: Dict[str, Dict] = {
|
|
|
84
84
|
"default": 10000,
|
|
85
85
|
"format": "{:.0f}",
|
|
86
86
|
},
|
|
87
|
+
"interleaved_thinking": {
|
|
88
|
+
"name": "Interleaved Thinking",
|
|
89
|
+
"description": "Enable thinking between tool calls (Claude 4 only: Opus 4.5, Opus 4.1, Opus 4, Sonnet 4). Adds beta header. WARNING: On Vertex/Bedrock, this FAILS for non-Claude 4 models!",
|
|
90
|
+
"type": "boolean",
|
|
91
|
+
"default": False,
|
|
92
|
+
},
|
|
87
93
|
}
|
|
88
94
|
|
|
89
95
|
|
|
@@ -93,6 +99,42 @@ def _load_all_model_names() -> List[str]:
|
|
|
93
99
|
return list(models_config.keys())
|
|
94
100
|
|
|
95
101
|
|
|
102
|
+
def _get_setting_choices(
|
|
103
|
+
setting_key: str, model_name: Optional[str] = None
|
|
104
|
+
) -> List[str]:
|
|
105
|
+
"""Get the available choices for a setting, filtered by model capabilities.
|
|
106
|
+
|
|
107
|
+
For reasoning_effort, only codex models support 'xhigh' - regular GPT-5.2
|
|
108
|
+
models are capped at 'high'.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
setting_key: The setting name (e.g., 'reasoning_effort', 'verbosity')
|
|
112
|
+
model_name: Optional model name to filter choices for
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
List of valid choices for this setting and model combination.
|
|
116
|
+
"""
|
|
117
|
+
setting_def = SETTING_DEFINITIONS.get(setting_key, {})
|
|
118
|
+
if setting_def.get("type") != "choice":
|
|
119
|
+
return []
|
|
120
|
+
|
|
121
|
+
base_choices = setting_def.get("choices", [])
|
|
122
|
+
|
|
123
|
+
# For reasoning_effort, filter 'xhigh' based on model support
|
|
124
|
+
if setting_key == "reasoning_effort" and model_name:
|
|
125
|
+
models_config = ModelFactory.load_config()
|
|
126
|
+
model_config = models_config.get(model_name, {})
|
|
127
|
+
|
|
128
|
+
# Check if model supports xhigh reasoning
|
|
129
|
+
supports_xhigh = model_config.get("supports_xhigh_reasoning", False)
|
|
130
|
+
|
|
131
|
+
if not supports_xhigh:
|
|
132
|
+
# Remove xhigh from choices for non-codex models
|
|
133
|
+
return [c for c in base_choices if c != "xhigh"]
|
|
134
|
+
|
|
135
|
+
return base_choices
|
|
136
|
+
|
|
137
|
+
|
|
96
138
|
class ModelSettingsMenu:
|
|
97
139
|
"""Interactive TUI for model settings configuration.
|
|
98
140
|
|
|
@@ -427,7 +469,8 @@ class ModelSettingsMenu:
|
|
|
427
469
|
if setting_def.get("type") == "choice":
|
|
428
470
|
lines.append(("bold", " Options:"))
|
|
429
471
|
lines.append(("", "\n"))
|
|
430
|
-
|
|
472
|
+
# Get filtered choices based on model capabilities
|
|
473
|
+
choices = _get_setting_choices(setting_key, self.selected_model)
|
|
431
474
|
lines.append(
|
|
432
475
|
(
|
|
433
476
|
"fg:ansibrightblack",
|
|
@@ -514,8 +557,11 @@ class ModelSettingsMenu:
|
|
|
514
557
|
if current is not None:
|
|
515
558
|
self.edit_value = current
|
|
516
559
|
elif setting_def.get("type") == "choice":
|
|
517
|
-
# For choice settings, start with the default
|
|
518
|
-
|
|
560
|
+
# For choice settings, start with the default (using filtered choices)
|
|
561
|
+
choices = _get_setting_choices(setting_key, self.selected_model)
|
|
562
|
+
self.edit_value = setting_def.get(
|
|
563
|
+
"default", choices[0] if choices else None
|
|
564
|
+
)
|
|
519
565
|
elif setting_def.get("type") == "boolean":
|
|
520
566
|
# For boolean settings, start with the default
|
|
521
567
|
self.edit_value = setting_def.get("default", False)
|
|
@@ -541,8 +587,8 @@ class ModelSettingsMenu:
|
|
|
541
587
|
setting_def = SETTING_DEFINITIONS[setting_key]
|
|
542
588
|
|
|
543
589
|
if setting_def.get("type") == "choice":
|
|
544
|
-
# Cycle through choices
|
|
545
|
-
choices =
|
|
590
|
+
# Cycle through filtered choices based on model capabilities
|
|
591
|
+
choices = _get_setting_choices(setting_key, self.selected_model)
|
|
546
592
|
current_idx = (
|
|
547
593
|
choices.index(self.edit_value) if self.edit_value in choices else 0
|
|
548
594
|
)
|
|
@@ -789,6 +835,11 @@ class ModelSettingsMenu:
|
|
|
789
835
|
sys.stdout.flush()
|
|
790
836
|
set_awaiting_user_input(False)
|
|
791
837
|
|
|
838
|
+
# Clear exit message
|
|
839
|
+
from code_puppy.messaging import emit_info
|
|
840
|
+
|
|
841
|
+
emit_info("✓ Exited model settings")
|
|
842
|
+
|
|
792
843
|
return self.result_changed
|
|
793
844
|
|
|
794
845
|
|