code-puppy 0.0.287__py3-none-any.whl → 0.0.323__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 (110) hide show
  1. code_puppy/__init__.py +3 -1
  2. code_puppy/agents/agent_code_puppy.py +5 -4
  3. code_puppy/agents/agent_creator_agent.py +22 -18
  4. code_puppy/agents/agent_manager.py +2 -2
  5. code_puppy/agents/base_agent.py +496 -102
  6. code_puppy/callbacks.py +8 -0
  7. code_puppy/chatgpt_codex_client.py +283 -0
  8. code_puppy/cli_runner.py +795 -0
  9. code_puppy/command_line/add_model_menu.py +19 -16
  10. code_puppy/command_line/attachments.py +10 -5
  11. code_puppy/command_line/autosave_menu.py +269 -41
  12. code_puppy/command_line/colors_menu.py +515 -0
  13. code_puppy/command_line/command_handler.py +10 -24
  14. code_puppy/command_line/config_commands.py +106 -25
  15. code_puppy/command_line/core_commands.py +32 -20
  16. code_puppy/command_line/mcp/add_command.py +3 -16
  17. code_puppy/command_line/mcp/base.py +0 -3
  18. code_puppy/command_line/mcp/catalog_server_installer.py +15 -15
  19. code_puppy/command_line/mcp/custom_server_form.py +66 -5
  20. code_puppy/command_line/mcp/custom_server_installer.py +17 -17
  21. code_puppy/command_line/mcp/edit_command.py +15 -22
  22. code_puppy/command_line/mcp/handler.py +7 -2
  23. code_puppy/command_line/mcp/help_command.py +2 -2
  24. code_puppy/command_line/mcp/install_command.py +10 -14
  25. code_puppy/command_line/mcp/install_menu.py +2 -6
  26. code_puppy/command_line/mcp/list_command.py +2 -2
  27. code_puppy/command_line/mcp/logs_command.py +174 -65
  28. code_puppy/command_line/mcp/remove_command.py +2 -2
  29. code_puppy/command_line/mcp/restart_command.py +7 -2
  30. code_puppy/command_line/mcp/search_command.py +16 -10
  31. code_puppy/command_line/mcp/start_all_command.py +16 -6
  32. code_puppy/command_line/mcp/start_command.py +12 -10
  33. code_puppy/command_line/mcp/status_command.py +4 -5
  34. code_puppy/command_line/mcp/stop_all_command.py +5 -1
  35. code_puppy/command_line/mcp/stop_command.py +6 -4
  36. code_puppy/command_line/mcp/test_command.py +2 -2
  37. code_puppy/command_line/mcp/wizard_utils.py +20 -16
  38. code_puppy/command_line/model_settings_menu.py +53 -7
  39. code_puppy/command_line/motd.py +1 -1
  40. code_puppy/command_line/pin_command_completion.py +82 -7
  41. code_puppy/command_line/prompt_toolkit_completion.py +32 -9
  42. code_puppy/command_line/session_commands.py +11 -4
  43. code_puppy/config.py +217 -53
  44. code_puppy/error_logging.py +118 -0
  45. code_puppy/gemini_code_assist.py +385 -0
  46. code_puppy/keymap.py +126 -0
  47. code_puppy/main.py +5 -745
  48. code_puppy/mcp_/__init__.py +17 -0
  49. code_puppy/mcp_/blocking_startup.py +63 -36
  50. code_puppy/mcp_/captured_stdio_server.py +1 -1
  51. code_puppy/mcp_/config_wizard.py +4 -4
  52. code_puppy/mcp_/dashboard.py +15 -6
  53. code_puppy/mcp_/managed_server.py +25 -5
  54. code_puppy/mcp_/manager.py +65 -0
  55. code_puppy/mcp_/mcp_logs.py +224 -0
  56. code_puppy/mcp_/registry.py +6 -6
  57. code_puppy/messaging/__init__.py +184 -2
  58. code_puppy/messaging/bus.py +610 -0
  59. code_puppy/messaging/commands.py +167 -0
  60. code_puppy/messaging/markdown_patches.py +57 -0
  61. code_puppy/messaging/message_queue.py +3 -3
  62. code_puppy/messaging/messages.py +470 -0
  63. code_puppy/messaging/renderers.py +43 -141
  64. code_puppy/messaging/rich_renderer.py +900 -0
  65. code_puppy/messaging/spinner/console_spinner.py +39 -2
  66. code_puppy/model_factory.py +292 -53
  67. code_puppy/model_utils.py +57 -48
  68. code_puppy/models.json +19 -5
  69. code_puppy/plugins/__init__.py +152 -10
  70. code_puppy/plugins/chatgpt_oauth/config.py +20 -12
  71. code_puppy/plugins/chatgpt_oauth/oauth_flow.py +5 -6
  72. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +3 -3
  73. code_puppy/plugins/chatgpt_oauth/test_plugin.py +30 -13
  74. code_puppy/plugins/chatgpt_oauth/utils.py +180 -65
  75. code_puppy/plugins/claude_code_oauth/config.py +15 -11
  76. code_puppy/plugins/claude_code_oauth/register_callbacks.py +28 -0
  77. code_puppy/plugins/claude_code_oauth/utils.py +6 -1
  78. code_puppy/plugins/example_custom_command/register_callbacks.py +2 -2
  79. code_puppy/plugins/oauth_puppy_html.py +3 -0
  80. code_puppy/plugins/shell_safety/agent_shell_safety.py +1 -134
  81. code_puppy/plugins/shell_safety/command_cache.py +156 -0
  82. code_puppy/plugins/shell_safety/register_callbacks.py +77 -3
  83. code_puppy/prompts/codex_system_prompt.md +310 -0
  84. code_puppy/pydantic_patches.py +131 -0
  85. code_puppy/session_storage.py +2 -1
  86. code_puppy/status_display.py +7 -5
  87. code_puppy/terminal_utils.py +126 -0
  88. code_puppy/tools/agent_tools.py +131 -70
  89. code_puppy/tools/browser/browser_control.py +10 -14
  90. code_puppy/tools/browser/browser_interactions.py +20 -28
  91. code_puppy/tools/browser/browser_locators.py +27 -29
  92. code_puppy/tools/browser/browser_navigation.py +9 -9
  93. code_puppy/tools/browser/browser_screenshot.py +12 -14
  94. code_puppy/tools/browser/browser_scripts.py +17 -29
  95. code_puppy/tools/browser/browser_workflows.py +24 -25
  96. code_puppy/tools/browser/camoufox_manager.py +22 -26
  97. code_puppy/tools/command_runner.py +410 -88
  98. code_puppy/tools/common.py +51 -38
  99. code_puppy/tools/file_modifications.py +98 -24
  100. code_puppy/tools/file_operations.py +113 -202
  101. code_puppy/version_checker.py +28 -13
  102. {code_puppy-0.0.287.data → code_puppy-0.0.323.data}/data/code_puppy/models.json +19 -5
  103. {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/METADATA +3 -8
  104. code_puppy-0.0.323.dist-info/RECORD +168 -0
  105. code_puppy/tui_state.py +0 -55
  106. code_puppy-0.0.287.dist-info/RECORD +0 -153
  107. {code_puppy-0.0.287.data → code_puppy-0.0.323.data}/data/code_puppy/models_dev_api.json +0 -0
  108. {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/WHEEL +0 -0
  109. {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/entry_points.txt +0 -0
  110. {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/licenses/LICENSE +0 -0
@@ -25,6 +25,7 @@ from prompt_toolkit.lexers import PygmentsLexer
25
25
  from prompt_toolkit.widgets import Frame, TextArea
26
26
  from pygments.lexers.data import JsonLexer
27
27
 
28
+ from code_puppy.messaging import emit_info, emit_success
28
29
  from code_puppy.tools.command_runner import set_awaiting_user_input
29
30
 
30
31
  # Example configurations for each server type
@@ -106,6 +107,10 @@ class CustomServerForm:
106
107
  # Focus state: 0=name, 1=type, 2=json
107
108
  self.focused_field = 0
108
109
 
110
+ # Status message for user feedback (e.g., "Save failed: ...")
111
+ self.status_message: Optional[str] = None
112
+ self.status_is_error: bool = False
113
+
109
114
  # Result
110
115
  self.result = None # "installed", "cancelled", None
111
116
 
@@ -136,6 +141,12 @@ class CustomServerForm:
136
141
  else:
137
142
  name_display = self.server_name if self.server_name else "(not set)"
138
143
  lines.append(("fg:ansibrightblack", f" {name_display}"))
144
+
145
+ # Show name validation hint inline
146
+ name_error = self._validate_server_name(self.server_name)
147
+ if name_error and self.server_name: # Only show if there's input
148
+ lines.append(("", "\n"))
149
+ lines.append(("fg:ansiyellow", f" ⚠ {name_error}"))
139
150
  lines.append(("", "\n\n"))
140
151
 
141
152
  # Server Type field
@@ -201,6 +212,16 @@ class CustomServerForm:
201
212
  lines.append(("fg:ansired", " Ctrl+C/Esc "))
202
213
  lines.append(("", "Cancel"))
203
214
 
215
+ # Status message bar - shows feedback for user actions
216
+ if self.status_message:
217
+ lines.append(("", "\n\n"))
218
+ lines.append(("bold", " ─" * 20))
219
+ lines.append(("", "\n"))
220
+ if self.status_is_error:
221
+ lines.append(("fg:ansired bold", f" ⚠️ {self.status_message}"))
222
+ else:
223
+ lines.append(("fg:ansigreen bold", f" ✓ {self.status_message}"))
224
+
204
225
  return lines
205
226
 
206
227
  def _render_preview(self) -> List:
@@ -269,6 +290,30 @@ class CustomServerForm:
269
290
 
270
291
  return lines
271
292
 
293
+ def _validate_server_name(self, name: str) -> Optional[str]:
294
+ """Validate server name format.
295
+
296
+ Args:
297
+ name: Server name to validate
298
+
299
+ Returns:
300
+ Error message if invalid, None if valid
301
+ """
302
+ if not name or not name.strip():
303
+ return "Server name is required"
304
+
305
+ name = name.strip()
306
+
307
+ # Check for valid characters (alphanumeric, hyphens, underscores)
308
+ if not name.replace("-", "").replace("_", "").isalnum():
309
+ return "Name must be alphanumeric (hyphens/underscores OK)"
310
+
311
+ # Check for reasonable length
312
+ if len(name) > 64:
313
+ return "Name too long (max 64 characters)"
314
+
315
+ return None
316
+
272
317
  def _validate_json(self) -> bool:
273
318
  """Validate the current JSON configuration.
274
319
 
@@ -304,11 +349,17 @@ class CustomServerForm:
304
349
  from code_puppy.config import MCP_SERVERS_FILE
305
350
  from code_puppy.mcp_.managed_server import ServerConfig
306
351
 
307
- if not self.server_name.strip():
308
- self.validation_error = "Server name is required"
352
+ # Validate server name first
353
+ name_error = self._validate_server_name(self.server_name)
354
+ if name_error:
355
+ self.validation_error = name_error
356
+ self.status_message = f"Save failed: {name_error}"
357
+ self.status_is_error = True
309
358
  return False
310
359
 
311
360
  if not self._validate_json():
361
+ self.status_message = f"Save failed: {self.validation_error}"
362
+ self.status_is_error = True
312
363
  return False
313
364
 
314
365
  server_name = self.server_name.strip()
@@ -329,6 +380,10 @@ class CustomServerForm:
329
380
 
330
381
  if not server_id:
331
382
  self.validation_error = "Failed to register server"
383
+ self.status_message = (
384
+ "Save failed: Could not register server (name may already exist)"
385
+ )
386
+ self.status_is_error = True
332
387
  return False
333
388
 
334
389
  # Save to mcp_servers.json for persistence
@@ -363,6 +418,8 @@ class CustomServerForm:
363
418
 
364
419
  except Exception as e:
365
420
  self.validation_error = f"Error: {e}"
421
+ self.status_message = f"Save failed: {e}"
422
+ self.status_is_error = True
366
423
  return False
367
424
 
368
425
  def run(self) -> bool:
@@ -550,10 +607,14 @@ class CustomServerForm:
550
607
  # Handle result
551
608
  if self.result == "installed":
552
609
  if self.edit_mode:
553
- print(f"\n ✅ Successfully updated server '{self.server_name}'!")
610
+ emit_success(
611
+ f"\n ✅ Successfully updated server '{self.server_name}'!"
612
+ )
554
613
  else:
555
- print(f"\n ✅ Successfully added custom server '{self.server_name}'!")
556
- print(f" Use '/mcp start {self.server_name}' to start the server.\n")
614
+ emit_success(
615
+ f"\n Successfully added custom server '{self.server_name}'!"
616
+ )
617
+ emit_info(f" Use '/mcp start {self.server_name}' to start the server.\n")
557
618
  return True
558
619
 
559
620
  return False
@@ -7,7 +7,7 @@ custom MCP servers with JSON configuration.
7
7
  import json
8
8
  import os
9
9
 
10
- from code_puppy.messaging import emit_error, emit_warning
10
+ from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
11
11
 
12
12
  # Example configurations for each server type
13
13
  CUSTOM_SERVER_EXAMPLES = {
@@ -53,8 +53,8 @@ def prompt_and_install_custom_server(manager) -> bool:
53
53
 
54
54
  from .utils import find_server_id_by_name
55
55
 
56
- print("\n➕ Add Custom MCP Server\n")
57
- print(" Configure your own MCP server using JSON.\n")
56
+ emit_info("\n➕ Add Custom MCP Server\n")
57
+ emit_info(" Configure your own MCP server using JSON.\n")
58
58
 
59
59
  # Get server name
60
60
  try:
@@ -63,7 +63,7 @@ def prompt_and_install_custom_server(manager) -> bool:
63
63
  emit_warning("Server name is required")
64
64
  return False
65
65
  except (KeyboardInterrupt, EOFError):
66
- print("")
66
+ emit_info("")
67
67
  emit_warning("Cancelled")
68
68
  return False
69
69
 
@@ -78,20 +78,20 @@ def prompt_and_install_custom_server(manager) -> bool:
78
78
  emit_warning("Cancelled")
79
79
  return False
80
80
  except (KeyboardInterrupt, EOFError):
81
- print("")
81
+ emit_info("")
82
82
  emit_warning("Cancelled")
83
83
  return False
84
84
 
85
85
  # Select server type
86
- print("\n Select server type:\n")
87
- print(" 1. 📟 stdio - Local command (npx, python, uvx, etc.)")
88
- print(" 2. 🌐 http - HTTP endpoint")
89
- print(" 3. 📡 sse - Server-Sent Events\n")
86
+ emit_info("\n Select server type:\n")
87
+ emit_info(" 1. 📟 stdio - Local command (npx, python, uvx, etc.)")
88
+ emit_info(" 2. 🌐 http - HTTP endpoint")
89
+ emit_info(" 3. 📡 sse - Server-Sent Events\n")
90
90
 
91
91
  try:
92
92
  type_choice = input(" Enter choice [1-3]: ").strip()
93
93
  except (KeyboardInterrupt, EOFError):
94
- print("")
94
+ emit_info("")
95
95
  emit_warning("Cancelled")
96
96
  return False
97
97
 
@@ -103,13 +103,13 @@ def prompt_and_install_custom_server(manager) -> bool:
103
103
 
104
104
  # Show example for selected type
105
105
  example = CUSTOM_SERVER_EXAMPLES.get(server_type, "{}")
106
- print(f"\n Example {server_type} configuration:\n")
106
+ emit_info(f"\n Example {server_type} configuration:\n")
107
107
  for line in example.split("\n"):
108
- print(f" {line}")
109
- print("")
108
+ emit_info(f" {line}")
109
+ emit_info("")
110
110
 
111
111
  # Get JSON configuration
112
- print(" Enter your JSON configuration (paste and press Enter twice):\n")
112
+ emit_info(" Enter your JSON configuration (paste and press Enter twice):\n")
113
113
 
114
114
  json_lines = []
115
115
  empty_count = 0
@@ -125,7 +125,7 @@ def prompt_and_install_custom_server(manager) -> bool:
125
125
  empty_count = 0
126
126
  json_lines.append(line)
127
127
  except (KeyboardInterrupt, EOFError):
128
- print("")
128
+ emit_info("")
129
129
  emit_warning("Cancelled")
130
130
  return False
131
131
 
@@ -187,8 +187,8 @@ def prompt_and_install_custom_server(manager) -> bool:
187
187
  with open(MCP_SERVERS_FILE, "w") as f:
188
188
  json.dump(data, f, indent=2)
189
189
 
190
- print(f"\n ✅ Successfully added custom server '{server_name}'!")
191
- print(f" Use '/mcp start {server_name}' to start the server.\n")
190
+ emit_success(f"\n ✅ Successfully added custom server '{server_name}'!")
191
+ emit_info(f" Use '/mcp start {server_name}' to start the server.\n")
192
192
  return True
193
193
 
194
194
  except Exception as e:
@@ -8,9 +8,10 @@ import logging
8
8
  import os
9
9
  from typing import List, Optional
10
10
 
11
+ from rich.text import Text
12
+
11
13
  from code_puppy.config import MCP_SERVERS_FILE
12
- from code_puppy.messaging import emit_info
13
- from code_puppy.tui_state import is_tui_mode
14
+ from code_puppy.messaging import emit_error, emit_info, emit_warning
14
15
 
15
16
  from .base import MCPCommandBase
16
17
  from .custom_server_form import run_custom_server_form
@@ -37,18 +38,10 @@ class EditCommand(MCPCommandBase):
37
38
  group_id = self.generate_group_id()
38
39
 
39
40
  try:
40
- # If in TUI mode, show message
41
- if is_tui_mode():
42
- emit_info(
43
- "In TUI mode, use Ctrl+T to manage MCP servers",
44
- message_group=group_id,
45
- )
46
- return
47
-
48
41
  # Need a server name
49
42
  if not args:
50
43
  emit_info(
51
- "[yellow]Usage: /mcp edit <server_name>[/yellow]",
44
+ Text.from_markup("[yellow]Usage: /mcp edit <server_name>[/yellow]"),
52
45
  message_group=group_id,
53
46
  )
54
47
  emit_info(
@@ -86,7 +79,7 @@ class EditCommand(MCPCommandBase):
86
79
 
87
80
  except Exception as e:
88
81
  logger.error(f"Error editing server: {e}")
89
- emit_info(f"[red]Error: {e}[/red]", message_group=group_id)
82
+ emit_error(f"Error: {e}", message_group=group_id)
90
83
 
91
84
  def _load_server_config(
92
85
  self, server_name: str, group_id: str
@@ -101,8 +94,8 @@ class EditCommand(MCPCommandBase):
101
94
  Tuple of (server_type, config_dict) or None if not found
102
95
  """
103
96
  if not os.path.exists(MCP_SERVERS_FILE):
104
- emit_info(
105
- "[red]No MCP servers configured yet.[/red]",
97
+ emit_error(
98
+ "No MCP servers configured yet.",
106
99
  message_group=group_id,
107
100
  )
108
101
  emit_info(
@@ -118,14 +111,14 @@ class EditCommand(MCPCommandBase):
118
111
  servers = data.get("mcp_servers", {})
119
112
 
120
113
  if server_name not in servers:
121
- emit_info(
122
- f"[red]Server '{server_name}' not found.[/red]",
114
+ emit_error(
115
+ f"Server '{server_name}' not found.",
123
116
  message_group=group_id,
124
117
  )
125
118
  # Show available servers
126
119
  if servers:
127
- emit_info(
128
- "\n[yellow]Available servers:[/yellow]",
120
+ emit_warning(
121
+ "\nAvailable servers:",
129
122
  message_group=group_id,
130
123
  )
131
124
  for name in sorted(servers.keys()):
@@ -142,14 +135,14 @@ class EditCommand(MCPCommandBase):
142
135
  return (server_type, config)
143
136
 
144
137
  except json.JSONDecodeError as e:
145
- emit_info(
146
- f"[red]Error reading config file: {e}[/red]",
138
+ emit_error(
139
+ f"Error reading config file: {e}",
147
140
  message_group=group_id,
148
141
  )
149
142
  return None
150
143
  except Exception as e:
151
- emit_info(
152
- f"[red]Error loading server config: {e}[/red]",
144
+ emit_error(
145
+ f"Error loading server config: {e}",
153
146
  message_group=group_id,
154
147
  )
155
148
  return None
@@ -8,6 +8,8 @@ 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
15
  from .add_command import AddCommand
@@ -103,7 +105,8 @@ class MCPCommandHandler(MCPCommandBase):
103
105
  args = shlex.split(args_str)
104
106
  except ValueError as e:
105
107
  emit_info(
106
- f"[red]Invalid command syntax: {e}[/red]", message_group=group_id
108
+ Text.from_markup(f"[red]Invalid command syntax: {e}[/red]"),
109
+ message_group=group_id,
107
110
  )
108
111
  return True
109
112
 
@@ -121,7 +124,9 @@ class MCPCommandHandler(MCPCommandBase):
121
124
  return True
122
125
  else:
123
126
  emit_info(
124
- f"[yellow]Unknown MCP subcommand: {subcommand}[/yellow]",
127
+ Text.from_markup(
128
+ f"[yellow]Unknown MCP subcommand: {subcommand}[/yellow]"
129
+ ),
125
130
  message_group=group_id,
126
131
  )
127
132
  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
 
@@ -148,4 +148,4 @@ class HelpCommand(MCPCommandBase):
148
148
 
149
149
  except Exception as e:
150
150
  logger.error(f"Error showing help: {e}")
151
- emit_info(f"[red]Error showing help: {e}[/red]", message_group=group_id)
151
+ emit_error(f"Error showing help: {e}", message_group=group_id)
@@ -5,8 +5,9 @@ MCP Install Command - Installs pre-configured MCP servers from the registry.
5
5
  import logging
6
6
  from typing import List, Optional
7
7
 
8
- from code_puppy.messaging import emit_info
9
- from code_puppy.tui_state import is_tui_mode
8
+ from rich.text import Text
9
+
10
+ from code_puppy.messaging import emit_error, emit_info
10
11
 
11
12
  from .base import MCPCommandBase
12
13
  from .install_menu import run_mcp_install_menu
@@ -34,14 +35,6 @@ class InstallCommand(MCPCommandBase):
34
35
  group_id = self.generate_group_id()
35
36
 
36
37
  try:
37
- # If in TUI mode, show message to use Ctrl+T
38
- if is_tui_mode():
39
- emit_info(
40
- "In TUI mode, use Ctrl+T to open the MCP Install Wizard",
41
- message_group=group_id,
42
- )
43
- return
44
-
45
38
  # In interactive mode, use the menu-based browser
46
39
  if not args:
47
40
  # No args - launch interactive menu
@@ -159,7 +152,9 @@ class InstallCommand(MCPCommandBase):
159
152
  required_env_vars = selected_server.get_environment_vars()
160
153
  if required_env_vars:
161
154
  emit_info(
162
- "\n[yellow]Required Environment Variables:[/yellow]",
155
+ Text.from_markup(
156
+ "\n[yellow]Required Environment Variables:[/yellow]"
157
+ ),
163
158
  message_group=group_id,
164
159
  )
165
160
  for var in required_env_vars:
@@ -169,7 +164,7 @@ class InstallCommand(MCPCommandBase):
169
164
  current_value = os.environ.get(var, "")
170
165
  if current_value:
171
166
  emit_info(
172
- f" {var}: [green]Already set[/green]",
167
+ Text.from_markup(f" {var}: [green]Already set[/green]"),
173
168
  message_group=group_id,
174
169
  )
175
170
  env_vars[var] = current_value
@@ -182,7 +177,8 @@ class InstallCommand(MCPCommandBase):
182
177
  required_cmd_args = selected_server.get_command_line_args()
183
178
  if required_cmd_args:
184
179
  emit_info(
185
- "\n[yellow]Command Line Arguments:[/yellow]", message_group=group_id
180
+ Text.from_markup("\n[yellow]Command Line Arguments:[/yellow]"),
181
+ message_group=group_id,
186
182
  )
187
183
  for arg_config in required_cmd_args:
188
184
  name = arg_config.get("name", "")
@@ -214,5 +210,5 @@ class InstallCommand(MCPCommandBase):
214
210
  return False
215
211
  except Exception as e:
216
212
  logger.error(f"Error installing from catalog: {e}")
217
- emit_info(f"[red]Installation error: {e}[/red]", message_group=group_id)
213
+ emit_error(f"Installation error: {e}", message_group=group_id)
218
214
  return False
@@ -16,7 +16,7 @@ from prompt_toolkit.layout import Dimension, Layout, VSplit, Window
16
16
  from prompt_toolkit.layout.controls import FormattedTextControl
17
17
  from prompt_toolkit.widgets import Frame
18
18
 
19
- from code_puppy.messaging import emit_error
19
+ from code_puppy.messaging import emit_error, emit_warning
20
20
  from code_puppy.tools.command_runner import set_awaiting_user_input
21
21
 
22
22
  from .catalog_server_installer import (
@@ -501,11 +501,7 @@ class MCPInstallMenu:
501
501
  True if a server was installed, False otherwise
502
502
  """
503
503
  if not self.categories:
504
- set_awaiting_user_input(True)
505
- try:
506
- print("No MCP server catalog available.")
507
- finally:
508
- set_awaiting_user_input(False)
504
+ emit_warning("No MCP server catalog available.")
509
505
  return False
510
506
 
511
507
  # Build UI
@@ -9,7 +9,7 @@ from rich.table import Table
9
9
  from rich.text import Text
10
10
 
11
11
  from code_puppy.mcp_.managed_server import ServerState
12
- from code_puppy.messaging import emit_info
12
+ from code_puppy.messaging import emit_error, emit_info
13
13
 
14
14
  from .base import MCPCommandBase
15
15
  from .utils import format_state_indicator, format_uptime
@@ -91,4 +91,4 @@ class ListCommand(MCPCommandBase):
91
91
 
92
92
  except Exception as e:
93
93
  logger.error(f"Error listing MCP servers: {e}")
94
- emit_info(f"[red]Error listing servers: {e}[/red]", message_group=group_id)
94
+ emit_error(f"Error listing servers: {e}", message_group=group_id)