code-puppy 0.0.135__py3-none-any.whl → 0.0.137__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 (61) hide show
  1. code_puppy/agent.py +15 -17
  2. code_puppy/agents/agent_manager.py +320 -9
  3. code_puppy/agents/base_agent.py +58 -2
  4. code_puppy/agents/runtime_manager.py +68 -42
  5. code_puppy/command_line/command_handler.py +82 -33
  6. code_puppy/command_line/mcp/__init__.py +10 -0
  7. code_puppy/command_line/mcp/add_command.py +183 -0
  8. code_puppy/command_line/mcp/base.py +35 -0
  9. code_puppy/command_line/mcp/handler.py +133 -0
  10. code_puppy/command_line/mcp/help_command.py +146 -0
  11. code_puppy/command_line/mcp/install_command.py +176 -0
  12. code_puppy/command_line/mcp/list_command.py +94 -0
  13. code_puppy/command_line/mcp/logs_command.py +126 -0
  14. code_puppy/command_line/mcp/remove_command.py +82 -0
  15. code_puppy/command_line/mcp/restart_command.py +92 -0
  16. code_puppy/command_line/mcp/search_command.py +117 -0
  17. code_puppy/command_line/mcp/start_all_command.py +126 -0
  18. code_puppy/command_line/mcp/start_command.py +98 -0
  19. code_puppy/command_line/mcp/status_command.py +185 -0
  20. code_puppy/command_line/mcp/stop_all_command.py +109 -0
  21. code_puppy/command_line/mcp/stop_command.py +79 -0
  22. code_puppy/command_line/mcp/test_command.py +107 -0
  23. code_puppy/command_line/mcp/utils.py +129 -0
  24. code_puppy/command_line/mcp/wizard_utils.py +259 -0
  25. code_puppy/command_line/model_picker_completion.py +21 -4
  26. code_puppy/command_line/prompt_toolkit_completion.py +9 -0
  27. code_puppy/config.py +5 -5
  28. code_puppy/main.py +23 -17
  29. code_puppy/mcp/__init__.py +42 -16
  30. code_puppy/mcp/async_lifecycle.py +51 -49
  31. code_puppy/mcp/blocking_startup.py +125 -113
  32. code_puppy/mcp/captured_stdio_server.py +63 -70
  33. code_puppy/mcp/circuit_breaker.py +63 -47
  34. code_puppy/mcp/config_wizard.py +169 -136
  35. code_puppy/mcp/dashboard.py +79 -71
  36. code_puppy/mcp/error_isolation.py +147 -100
  37. code_puppy/mcp/examples/retry_example.py +55 -42
  38. code_puppy/mcp/health_monitor.py +152 -141
  39. code_puppy/mcp/managed_server.py +100 -93
  40. code_puppy/mcp/manager.py +168 -156
  41. code_puppy/mcp/registry.py +148 -110
  42. code_puppy/mcp/retry_manager.py +63 -61
  43. code_puppy/mcp/server_registry_catalog.py +271 -225
  44. code_puppy/mcp/status_tracker.py +80 -80
  45. code_puppy/mcp/system_tools.py +47 -52
  46. code_puppy/messaging/message_queue.py +20 -13
  47. code_puppy/messaging/renderers.py +30 -15
  48. code_puppy/state_management.py +103 -0
  49. code_puppy/tui/app.py +64 -7
  50. code_puppy/tui/components/chat_view.py +3 -3
  51. code_puppy/tui/components/human_input_modal.py +12 -8
  52. code_puppy/tui/screens/__init__.py +2 -2
  53. code_puppy/tui/screens/mcp_install_wizard.py +208 -179
  54. code_puppy/tui/tests/test_agent_command.py +3 -3
  55. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/METADATA +1 -1
  56. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/RECORD +60 -42
  57. code_puppy/command_line/mcp_commands.py +0 -1789
  58. {code_puppy-0.0.135.data → code_puppy-0.0.137.data}/data/code_puppy/models.json +0 -0
  59. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/WHEEL +0 -0
  60. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/entry_points.txt +0 -0
  61. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/licenses/LICENSE +0 -0
@@ -4,27 +4,28 @@ MCP Dashboard Implementation
4
4
  Provides visual status dashboard for MCP servers using Rich tables.
5
5
  """
6
6
 
7
- from datetime import datetime, timedelta
8
- from typing import Dict, List, Any, Optional
9
- from rich.table import Table
10
- from rich.console import Console
7
+ from datetime import datetime
8
+ from typing import Dict, List, Optional
9
+
11
10
  from rich import box
11
+ from rich.console import Console
12
+ from rich.table import Table
12
13
 
13
- from .status_tracker import ServerState, Event
14
14
  from .manager import get_mcp_manager
15
+ from .status_tracker import ServerState
15
16
 
16
17
 
17
18
  class MCPDashboard:
18
19
  """Visual dashboard for MCP server status monitoring"""
19
-
20
+
20
21
  def __init__(self):
21
22
  """Initialize the MCP Dashboard"""
22
23
  self.console = Console()
23
-
24
+
24
25
  def render_dashboard(self) -> Table:
25
26
  """
26
27
  Render the main MCP server status dashboard
27
-
28
+
28
29
  Returns:
29
30
  Table: Rich table with server status information
30
31
  """
@@ -34,9 +35,9 @@ class MCPDashboard:
34
35
  box=box.ROUNDED,
35
36
  show_header=True,
36
37
  header_style="bold blue",
37
- title_style="bold cyan"
38
+ title_style="bold cyan",
38
39
  )
39
-
40
+
40
41
  # Define columns
41
42
  table.add_column("Name", style="white", no_wrap=True, min_width=10)
42
43
  table.add_column("Type", style="white", no_wrap=True, width=8)
@@ -44,100 +45,107 @@ class MCPDashboard:
44
45
  table.add_column("Health", style="white", no_wrap=True, width=8)
45
46
  table.add_column("Uptime", style="white", no_wrap=True, width=10)
46
47
  table.add_column("Latency", style="white", no_wrap=True, width=10)
47
-
48
+
48
49
  # Get manager and server info
49
50
  try:
50
51
  manager = get_mcp_manager()
51
52
  servers = manager.list_servers()
52
-
53
+
53
54
  if not servers:
54
55
  # Empty state
55
56
  table.add_row(
56
- "[dim]No servers configured[/dim]",
57
- "-", "-", "-", "-", "-"
57
+ "[dim]No servers configured[/dim]", "-", "-", "-", "-", "-"
58
58
  )
59
59
  else:
60
60
  # Add row for each server
61
61
  for server in servers:
62
62
  row_data = self.render_server_row(server)
63
63
  table.add_row(*row_data)
64
-
64
+
65
65
  except Exception as e:
66
66
  # Error state
67
67
  table.add_row(
68
68
  "[red]Error loading servers[/red]",
69
- "-", "-", "-", "-", f"[red]{str(e)}[/red]"
69
+ "-",
70
+ "-",
71
+ "-",
72
+ "-",
73
+ f"[red]{str(e)}[/red]",
70
74
  )
71
-
75
+
72
76
  return table
73
-
77
+
74
78
  def render_server_row(self, server) -> List[str]:
75
79
  """
76
80
  Render a single server row for the dashboard
77
-
81
+
78
82
  Args:
79
83
  server: ServerInfo object with server details
80
-
84
+
81
85
  Returns:
82
86
  List[str]: Formatted row data for the table
83
87
  """
84
88
  # Server name
85
89
  name = server.name or server.id[:8]
86
-
90
+
87
91
  # Server type
88
92
  server_type = server.type.upper() if server.type else "UNK"
89
-
93
+
90
94
  # State indicator
91
95
  state_indicator = self.render_state_indicator(server.state)
92
-
96
+
93
97
  # Health indicator
94
98
  health_indicator = self.render_health_indicator(server.health)
95
-
99
+
96
100
  # Uptime
97
101
  uptime_str = self.format_uptime(server.start_time) if server.start_time else "-"
98
-
102
+
99
103
  # Latency
100
- latency_str = self.format_latency(server.latency_ms) if server.latency_ms is not None else "-"
101
-
104
+ latency_str = (
105
+ self.format_latency(server.latency_ms)
106
+ if server.latency_ms is not None
107
+ else "-"
108
+ )
109
+
102
110
  return [
103
111
  name,
104
112
  server_type,
105
113
  state_indicator,
106
114
  health_indicator,
107
115
  uptime_str,
108
- latency_str
116
+ latency_str,
109
117
  ]
110
-
118
+
111
119
  def render_health_indicator(self, health: Optional[Dict]) -> str:
112
120
  """
113
121
  Render health status indicator
114
-
122
+
115
123
  Args:
116
124
  health: Health status dictionary or None
117
-
125
+
118
126
  Returns:
119
127
  str: Formatted health indicator with color
120
128
  """
121
129
  if not health:
122
130
  return "[dim]?[/dim]"
123
-
124
- is_healthy = health.get('is_healthy', False)
125
- error = health.get('error')
126
-
131
+
132
+ is_healthy = health.get("is_healthy", False)
133
+ error = health.get("error")
134
+
127
135
  if is_healthy:
128
136
  return "[green]✓[/green]"
129
137
  elif error:
130
138
  return "[red]✗[/red]"
131
139
  else:
132
140
  return "[yellow]?[/yellow]"
133
-
141
+
134
142
  def render_state_indicator(self, state: ServerState) -> str:
135
143
  """
136
144
  Render server state indicator
137
-
145
+
138
146
  Args:
139
147
  state: Current server state
140
-
148
+
141
149
  Returns:
142
150
  str: Formatted state indicator with color and symbol
143
151
  """
@@ -149,68 +157,68 @@ class MCPDashboard:
149
157
  ServerState.STOPPING: "[yellow]⏳ Stop[/yellow]",
150
158
  ServerState.QUARANTINED: "[yellow]⏸ Quar[/yellow]",
151
159
  }
152
-
160
+
153
161
  return indicators.get(state, "[dim]? Unk[/dim]")
154
-
162
+
155
163
  def render_metrics_summary(self, metrics: Dict) -> str:
156
164
  """
157
165
  Render a summary of server metrics
158
-
166
+
159
167
  Args:
160
168
  metrics: Dictionary of server metrics
161
-
169
+
162
170
  Returns:
163
171
  str: Formatted metrics summary
164
172
  """
165
173
  if not metrics:
166
174
  return "No metrics"
167
-
175
+
168
176
  parts = []
169
-
177
+
170
178
  # Request count
171
- if 'request_count' in metrics:
179
+ if "request_count" in metrics:
172
180
  parts.append(f"Req: {metrics['request_count']}")
173
-
181
+
174
182
  # Error rate
175
- if 'error_rate' in metrics:
176
- error_rate = metrics['error_rate']
183
+ if "error_rate" in metrics:
184
+ error_rate = metrics["error_rate"]
177
185
  if error_rate > 0.1: # 10%
178
186
  parts.append(f"[red]Err: {error_rate:.1%}[/red]")
179
187
  elif error_rate > 0.05: # 5%
180
188
  parts.append(f"[yellow]Err: {error_rate:.1%}[/yellow]")
181
189
  else:
182
190
  parts.append(f"[green]Err: {error_rate:.1%}[/green]")
183
-
191
+
184
192
  # Response time
185
- if 'avg_response_time' in metrics:
186
- avg_time = metrics['avg_response_time']
193
+ if "avg_response_time" in metrics:
194
+ avg_time = metrics["avg_response_time"]
187
195
  parts.append(f"Avg: {avg_time:.0f}ms")
188
-
196
+
189
197
  return " | ".join(parts) if parts else "No data"
190
-
198
+
191
199
  def format_uptime(self, start_time: datetime) -> str:
192
200
  """
193
201
  Format uptime duration in human readable format
194
-
202
+
195
203
  Args:
196
204
  start_time: Server start timestamp
197
-
205
+
198
206
  Returns:
199
207
  str: Formatted uptime string (e.g., "2h 15m")
200
208
  """
201
209
  if not start_time:
202
210
  return "-"
203
-
211
+
204
212
  try:
205
213
  uptime = datetime.now() - start_time
206
-
214
+
207
215
  # Handle negative uptime (clock skew, etc.)
208
216
  if uptime.total_seconds() < 0:
209
217
  return "0s"
210
-
218
+
211
219
  # Format based on duration
212
220
  total_seconds = int(uptime.total_seconds())
213
-
221
+
214
222
  if total_seconds < 60: # Less than 1 minute
215
223
  return f"{total_seconds}s"
216
224
  elif total_seconds < 3600: # Less than 1 hour
@@ -234,23 +242,23 @@ class MCPDashboard:
234
242
  return f"{days}d {hours}h"
235
243
  else:
236
244
  return f"{days}d"
237
-
245
+
238
246
  except Exception:
239
247
  return "?"
240
-
248
+
241
249
  def format_latency(self, latency_ms: float) -> str:
242
250
  """
243
251
  Format latency in human readable format
244
-
252
+
245
253
  Args:
246
254
  latency_ms: Latency in milliseconds
247
-
255
+
248
256
  Returns:
249
257
  str: Formatted latency string with color coding
250
258
  """
251
259
  if latency_ms is None:
252
260
  return "-"
253
-
261
+
254
262
  try:
255
263
  if latency_ms < 0:
256
264
  return "invalid"
@@ -265,27 +273,27 @@ class MCPDashboard:
265
273
  else: # Very slow
266
274
  seconds = latency_ms / 1000
267
275
  return f"[red]{seconds:.1f}s[/red]"
268
-
276
+
269
277
  except (ValueError, TypeError):
270
278
  return "error"
271
-
279
+
272
280
  def print_dashboard(self) -> None:
273
281
  """Print the dashboard to console"""
274
282
  table = self.render_dashboard()
275
283
  self.console.print(table)
276
284
  self.console.print() # Add spacing
277
-
285
+
278
286
  def get_dashboard_string(self) -> str:
279
287
  """
280
288
  Get dashboard as a string for programmatic use
281
-
289
+
282
290
  Returns:
283
291
  str: Dashboard rendered as plain text
284
292
  """
285
293
  # Create a console that captures output
286
294
  console = Console(file=None, width=80)
287
-
295
+
288
296
  with console.capture() as capture:
289
297
  console.print(self.render_dashboard())
290
-
291
- return capture.get()
298
+
299
+ return capture.get()