code-puppy 0.0.343__tar.gz → 0.0.345__tar.gz

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 (183) hide show
  1. {code_puppy-0.0.343 → code_puppy-0.0.345}/PKG-INFO +1 -23
  2. {code_puppy-0.0.343 → code_puppy-0.0.345}/README.md +0 -21
  3. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/base_agent.py +37 -129
  4. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/cli_runner.py +0 -35
  5. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/add_model_menu.py +8 -9
  6. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/config_commands.py +0 -10
  7. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/catalog_server_installer.py +5 -6
  8. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/custom_server_form.py +54 -19
  9. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/custom_server_installer.py +8 -9
  10. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/handler.py +0 -2
  11. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/help_command.py +1 -5
  12. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/start_command.py +36 -18
  13. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/onboarding_slides.py +0 -1
  14. code_puppy-0.0.345/code_puppy/command_line/utils.py +93 -0
  15. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/config.py +0 -23
  16. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/async_lifecycle.py +35 -4
  17. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/managed_server.py +49 -20
  18. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/manager.py +81 -52
  19. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/message_queue.py +11 -23
  20. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/summarization_agent.py +1 -11
  21. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/agent_tools.py +11 -55
  22. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/vqa_agent.py +1 -7
  23. {code_puppy-0.0.343 → code_puppy-0.0.345}/pyproject.toml +1 -2
  24. code_puppy-0.0.343/code_puppy/command_line/mcp/add_command.py +0 -170
  25. code_puppy-0.0.343/code_puppy/command_line/utils.py +0 -39
  26. {code_puppy-0.0.343 → code_puppy-0.0.345}/.gitignore +0 -0
  27. {code_puppy-0.0.343 → code_puppy-0.0.345}/LICENSE +0 -0
  28. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/__init__.py +0 -0
  29. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/__main__.py +0 -0
  30. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/__init__.py +0 -0
  31. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_c_reviewer.py +0 -0
  32. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_code_puppy.py +0 -0
  33. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_code_reviewer.py +0 -0
  34. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_cpp_reviewer.py +0 -0
  35. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_creator_agent.py +0 -0
  36. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_golang_reviewer.py +0 -0
  37. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_javascript_reviewer.py +0 -0
  38. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_manager.py +0 -0
  39. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_planning.py +0 -0
  40. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_python_programmer.py +0 -0
  41. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_python_reviewer.py +0 -0
  42. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_qa_expert.py +0 -0
  43. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_qa_kitten.py +0 -0
  44. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_security_auditor.py +0 -0
  45. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/agent_typescript_reviewer.py +0 -0
  46. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/json_agent.py +0 -0
  47. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/agents/prompt_reviewer.py +0 -0
  48. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/callbacks.py +0 -0
  49. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/chatgpt_codex_client.py +0 -0
  50. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/claude_cache_client.py +0 -0
  51. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/__init__.py +0 -0
  52. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/attachments.py +0 -0
  53. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/autosave_menu.py +0 -0
  54. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/clipboard.py +0 -0
  55. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/colors_menu.py +0 -0
  56. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/command_handler.py +0 -0
  57. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/command_registry.py +0 -0
  58. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/core_commands.py +0 -0
  59. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/diff_menu.py +0 -0
  60. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/file_path_completion.py +0 -0
  61. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/load_context_completion.py +0 -0
  62. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/__init__.py +0 -0
  63. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/base.py +0 -0
  64. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/edit_command.py +0 -0
  65. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/install_command.py +0 -0
  66. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/install_menu.py +0 -0
  67. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/list_command.py +0 -0
  68. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/logs_command.py +0 -0
  69. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/remove_command.py +0 -0
  70. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/restart_command.py +0 -0
  71. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/search_command.py +0 -0
  72. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/start_all_command.py +0 -0
  73. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/status_command.py +0 -0
  74. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
  75. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/stop_command.py +0 -0
  76. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/test_command.py +0 -0
  77. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/utils.py +0 -0
  78. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
  79. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/mcp_completion.py +0 -0
  80. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/model_picker_completion.py +0 -0
  81. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/model_settings_menu.py +0 -0
  82. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/motd.py +0 -0
  83. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/onboarding_wizard.py +0 -0
  84. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/pin_command_completion.py +0 -0
  85. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
  86. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/command_line/session_commands.py +0 -0
  87. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/error_logging.py +0 -0
  88. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/gemini_code_assist.py +0 -0
  89. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/http_utils.py +0 -0
  90. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/keymap.py +0 -0
  91. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/main.py +0 -0
  92. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/__init__.py +0 -0
  93. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/blocking_startup.py +0 -0
  94. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/captured_stdio_server.py +0 -0
  95. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/circuit_breaker.py +0 -0
  96. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/config_wizard.py +0 -0
  97. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/dashboard.py +0 -0
  98. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/error_isolation.py +0 -0
  99. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/examples/retry_example.py +0 -0
  100. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/health_monitor.py +0 -0
  101. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/mcp_logs.py +0 -0
  102. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/registry.py +0 -0
  103. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/retry_manager.py +0 -0
  104. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/server_registry_catalog.py +0 -0
  105. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/status_tracker.py +0 -0
  106. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/mcp_/system_tools.py +0 -0
  107. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/__init__.py +0 -0
  108. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/bus.py +0 -0
  109. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/commands.py +0 -0
  110. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/markdown_patches.py +0 -0
  111. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/messages.py +0 -0
  112. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/queue_console.py +0 -0
  113. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/renderers.py +0 -0
  114. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/rich_renderer.py +0 -0
  115. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/spinner/__init__.py +0 -0
  116. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/spinner/console_spinner.py +0 -0
  117. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/messaging/spinner/spinner_base.py +0 -0
  118. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/model_factory.py +0 -0
  119. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/model_utils.py +0 -0
  120. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/models.json +0 -0
  121. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/models_dev_api.json +0 -0
  122. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/models_dev_parser.py +0 -0
  123. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/__init__.py +0 -0
  124. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/__init__.py +0 -0
  125. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/accounts.py +0 -0
  126. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/antigravity_model.py +0 -0
  127. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/config.py +0 -0
  128. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/constants.py +0 -0
  129. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/oauth.py +0 -0
  130. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/register_callbacks.py +0 -0
  131. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/storage.py +0 -0
  132. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/test_plugin.py +0 -0
  133. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/token.py +0 -0
  134. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/transport.py +0 -0
  135. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/antigravity_oauth/utils.py +0 -0
  136. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
  137. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
  138. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
  139. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
  140. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
  141. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
  142. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
  143. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
  144. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
  145. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
  146. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
  147. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
  148. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
  149. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
  150. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
  151. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/example_custom_command/README.md +0 -0
  152. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
  153. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
  154. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
  155. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/oauth_puppy_html.py +0 -0
  156. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/shell_safety/__init__.py +0 -0
  157. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
  158. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
  159. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
  160. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/prompts/codex_system_prompt.md +0 -0
  161. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/pydantic_patches.py +0 -0
  162. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/reopenable_async_client.py +0 -0
  163. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/round_robin_model.py +0 -0
  164. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/session_storage.py +0 -0
  165. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/status_display.py +0 -0
  166. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/terminal_utils.py +0 -0
  167. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/__init__.py +0 -0
  168. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/__init__.py +0 -0
  169. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/browser_control.py +0 -0
  170. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/browser_interactions.py +0 -0
  171. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/browser_locators.py +0 -0
  172. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/browser_navigation.py +0 -0
  173. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/browser_screenshot.py +0 -0
  174. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/browser_scripts.py +0 -0
  175. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/browser_workflows.py +0 -0
  176. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/browser/camoufox_manager.py +0 -0
  177. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/command_runner.py +0 -0
  178. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/common.py +0 -0
  179. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/file_modifications.py +0 -0
  180. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/file_operations.py +0 -0
  181. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/tools/tools_content.py +0 -0
  182. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/uvx_detection.py +0 -0
  183. {code_puppy-0.0.343 → code_puppy-0.0.345}/code_puppy/version_checker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.343
3
+ Version: 0.0.345
4
4
  Summary: Code generation agent
5
5
  Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
6
6
  Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
@@ -16,7 +16,6 @@ Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Topic :: Software Development :: Code Generators
17
17
  Requires-Python: <3.14,>=3.11
18
18
  Requires-Dist: camoufox>=0.4.11
19
- Requires-Dist: dbos>=2.5.0
20
19
  Requires-Dist: fastapi>=0.111.0
21
20
  Requires-Dist: httpx[http2]>=0.24.1
22
21
  Requires-Dist: json-repair>=0.46.2
@@ -174,27 +173,6 @@ These providers are automatically configured with correct OpenAI-compatible endp
174
173
  - **⚠️ Unsupported Providers** - Providers like Amazon Bedrock and Google Vertex that require special authentication are clearly marked
175
174
  - **⚠️ No Tool Calling** - Models without tool calling support show a big warning since they can't use Code Puppy's file/shell tools
176
175
 
177
- ### Durable Execution
178
-
179
- Code Puppy now supports **[DBOS](https://github.com/dbos-inc/dbos-transact-py)** durable execution.
180
-
181
- When enabled, every agent is automatically wrapped as a `DBOSAgent`, checkpointing key interactions (including agent inputs, LLM responses, MCP calls, and tool calls) in a database for durability and recovery.
182
-
183
- You can toggle DBOS via either of these options:
184
-
185
- - CLI config (persists): `/set enable_dbos true` (or `false` to disable)
186
-
187
-
188
- Config takes precedence if set; otherwise the environment variable is used.
189
-
190
- ### Configuration
191
-
192
- The following environment variables control DBOS behavior:
193
- - `DBOS_CONDUCTOR_KEY`: If set, Code Puppy connects to the [DBOS Management Console](https://console.dbos.dev/). Make sure you first register an app named `dbos-code-puppy` on the console to generate a Conductor key. Default: `None`.
194
- - `DBOS_LOG_LEVEL`: Logging verbosity: `CRITICAL`, `ERROR`, `WARNING`, `INFO`, or `DEBUG`. Default: `ERROR`.
195
- - `DBOS_SYSTEM_DATABASE_URL`: Database URL used by DBOS. Can point to a local SQLite file or a Postgres instance. Example: `postgresql://postgres:dbos@localhost:5432/postgres`. Default: `dbos_store.sqlite` file in the config directory.
196
- - `DBOS_APP_VERSION`: If set, Code Puppy uses it as the [DBOS application version](https://docs.dbos.dev/architecture#application-and-workflow-versions) and automatically tries to recover pending workflows for this version. Default: Code Puppy version + Unix timestamp in millisecond (disable automatic recovery).
197
-
198
176
  ### Custom Commands
199
177
  Create markdown files in `.claude/commands/`, `.github/prompts/`, or `.agents/commands/` to define custom slash commands. The filename becomes the command name and the content runs as a prompt.
200
178
 
@@ -133,27 +133,6 @@ These providers are automatically configured with correct OpenAI-compatible endp
133
133
  - **⚠️ Unsupported Providers** - Providers like Amazon Bedrock and Google Vertex that require special authentication are clearly marked
134
134
  - **⚠️ No Tool Calling** - Models without tool calling support show a big warning since they can't use Code Puppy's file/shell tools
135
135
 
136
- ### Durable Execution
137
-
138
- Code Puppy now supports **[DBOS](https://github.com/dbos-inc/dbos-transact-py)** durable execution.
139
-
140
- When enabled, every agent is automatically wrapped as a `DBOSAgent`, checkpointing key interactions (including agent inputs, LLM responses, MCP calls, and tool calls) in a database for durability and recovery.
141
-
142
- You can toggle DBOS via either of these options:
143
-
144
- - CLI config (persists): `/set enable_dbos true` (or `false` to disable)
145
-
146
-
147
- Config takes precedence if set; otherwise the environment variable is used.
148
-
149
- ### Configuration
150
-
151
- The following environment variables control DBOS behavior:
152
- - `DBOS_CONDUCTOR_KEY`: If set, Code Puppy connects to the [DBOS Management Console](https://console.dbos.dev/). Make sure you first register an app named `dbos-code-puppy` on the console to generate a Conductor key. Default: `None`.
153
- - `DBOS_LOG_LEVEL`: Logging verbosity: `CRITICAL`, `ERROR`, `WARNING`, `INFO`, or `DEBUG`. Default: `ERROR`.
154
- - `DBOS_SYSTEM_DATABASE_URL`: Database URL used by DBOS. Can point to a local SQLite file or a Postgres instance. Example: `postgresql://postgres:dbos@localhost:5432/postgres`. Default: `dbos_store.sqlite` file in the config directory.
155
- - `DBOS_APP_VERSION`: If set, Code Puppy uses it as the [DBOS application version](https://docs.dbos.dev/architecture#application-and-workflow-versions) and automatically tries to recover pending workflows for this version. Default: Code Puppy version + Unix timestamp in millisecond (disable automatic recovery).
156
-
157
136
  ### Custom Commands
158
137
  Create markdown files in `.claude/commands/`, `.github/prompts/`, or `.agents/commands/` to define custom slash commands. The filename becomes the command name and the content runs as a prompt.
159
138
 
@@ -24,7 +24,6 @@ from typing import (
24
24
  import mcp
25
25
  import pydantic
26
26
  import pydantic_ai.models
27
- from dbos import DBOS, SetWorkflowID
28
27
  from pydantic_ai import Agent as PydanticAgent
29
28
  from pydantic_ai import (
30
29
  BinaryContent,
@@ -35,7 +34,6 @@ from pydantic_ai import (
35
34
  UsageLimitExceeded,
36
35
  UsageLimits,
37
36
  )
38
- from pydantic_ai.durable_exec.dbos import DBOSAgent
39
37
  from pydantic_ai.messages import (
40
38
  ModelMessage,
41
39
  ModelRequest,
@@ -56,7 +54,6 @@ from code_puppy.config import (
56
54
  get_global_model_name,
57
55
  get_message_limit,
58
56
  get_protected_token_count,
59
- get_use_dbos,
60
57
  get_value,
61
58
  )
62
59
  from code_puppy.error_logging import log_error
@@ -1212,56 +1209,25 @@ class BaseAgent(ABC):
1212
1209
 
1213
1210
  self._last_model_name = resolved_model_name
1214
1211
  # expose for run_with_mcp
1215
- # Wrap it with DBOS, but handle MCP servers separately to avoid serialization issues
1216
1212
  global _reload_count
1217
1213
  _reload_count += 1
1218
- if get_use_dbos():
1219
- # Don't pass MCP servers to the agent constructor when using DBOS
1220
- # This prevents the "cannot pickle async_generator object" error
1221
- # MCP servers will be handled separately in run_with_mcp
1222
- agent_without_mcp = PydanticAgent(
1223
- model=model,
1224
- instructions=instructions,
1225
- output_type=str,
1226
- retries=3,
1227
- toolsets=[], # Don't include MCP servers here
1228
- history_processors=[self.message_history_accumulator],
1229
- model_settings=model_settings,
1230
- )
1231
-
1232
- # Register regular tools (non-MCP) on the new agent
1233
- agent_tools = self.get_available_tools()
1234
- register_tools_for_agent(agent_without_mcp, agent_tools)
1235
-
1236
- # Wrap with DBOS
1237
- dbos_agent = DBOSAgent(
1238
- agent_without_mcp, name=f"{self.name}-{_reload_count}"
1239
- )
1240
- self.pydantic_agent = dbos_agent
1241
- self._code_generation_agent = dbos_agent
1214
+ # Include filtered MCP servers in the agent
1215
+ p_agent = PydanticAgent(
1216
+ model=model,
1217
+ instructions=instructions,
1218
+ output_type=str,
1219
+ retries=3,
1220
+ toolsets=filtered_mcp_servers if filtered_mcp_servers else [],
1221
+ history_processors=[self.message_history_accumulator],
1222
+ model_settings=model_settings,
1223
+ )
1224
+ # Register regular tools on the agent
1225
+ agent_tools = self.get_available_tools()
1226
+ register_tools_for_agent(p_agent, agent_tools)
1242
1227
 
1243
- # Store filtered MCP servers separately for runtime use
1244
- self._mcp_servers = filtered_mcp_servers
1245
- else:
1246
- # Normal path without DBOS - include filtered MCP servers in the agent
1247
- # Re-create agent with filtered MCP servers
1248
- p_agent = PydanticAgent(
1249
- model=model,
1250
- instructions=instructions,
1251
- output_type=str,
1252
- retries=3,
1253
- toolsets=filtered_mcp_servers,
1254
- history_processors=[self.message_history_accumulator],
1255
- model_settings=model_settings,
1256
- )
1257
- # Register regular tools on the agent
1258
- agent_tools = self.get_available_tools()
1259
- register_tools_for_agent(p_agent, agent_tools)
1260
-
1261
- self.pydantic_agent = p_agent
1262
- self._code_generation_agent = p_agent
1263
- self._mcp_servers = filtered_mcp_servers
1264
- self._mcp_servers = mcp_servers
1228
+ self.pydantic_agent = p_agent
1229
+ self._code_generation_agent = p_agent
1230
+ self._mcp_servers = filtered_mcp_servers
1265
1231
  return self._code_generation_agent
1266
1232
 
1267
1233
  def _create_agent_with_output_type(self, output_type: Type[Any]) -> PydanticAgent:
@@ -1275,7 +1241,7 @@ class BaseAgent(ABC):
1275
1241
  output_type: The Pydantic model or type for structured output.
1276
1242
 
1277
1243
  Returns:
1278
- A configured PydanticAgent (or DBOSAgent wrapper) with the custom output_type.
1244
+ A configured PydanticAgent with the custom output_type.
1279
1245
  """
1280
1246
  from code_puppy.model_utils import prepare_prompt_for_model
1281
1247
  from code_puppy.tools import register_tools_for_agent
@@ -1302,38 +1268,19 @@ class BaseAgent(ABC):
1302
1268
  global _reload_count
1303
1269
  _reload_count += 1
1304
1270
 
1305
- if get_use_dbos():
1306
- temp_agent = PydanticAgent(
1307
- model=model,
1308
- instructions=instructions,
1309
- output_type=output_type,
1310
- retries=3,
1311
- toolsets=[],
1312
- history_processors=[self.message_history_accumulator],
1313
- model_settings=model_settings,
1314
- )
1315
- agent_tools = self.get_available_tools()
1316
- register_tools_for_agent(temp_agent, agent_tools)
1317
- dbos_agent = DBOSAgent(
1318
- temp_agent, name=f"{self.name}-structured-{_reload_count}"
1319
- )
1320
- return dbos_agent
1321
- else:
1322
- temp_agent = PydanticAgent(
1323
- model=model,
1324
- instructions=instructions,
1325
- output_type=output_type,
1326
- retries=3,
1327
- toolsets=mcp_servers,
1328
- history_processors=[self.message_history_accumulator],
1329
- model_settings=model_settings,
1330
- )
1331
- agent_tools = self.get_available_tools()
1332
- register_tools_for_agent(temp_agent, agent_tools)
1333
- return temp_agent
1271
+ temp_agent = PydanticAgent(
1272
+ model=model,
1273
+ instructions=instructions,
1274
+ output_type=output_type,
1275
+ retries=3,
1276
+ toolsets=mcp_servers,
1277
+ history_processors=[self.message_history_accumulator],
1278
+ model_settings=model_settings,
1279
+ )
1280
+ agent_tools = self.get_available_tools()
1281
+ register_tools_for_agent(temp_agent, agent_tools)
1282
+ return temp_agent
1334
1283
 
1335
- # It's okay to decorate it with DBOS.step even if not using DBOS; the decorator is a no-op in that case.
1336
- @DBOS.step()
1337
1284
  def message_history_accumulator(self, ctx: RunContext, messages: List[Any]):
1338
1285
  _message_history = self.get_message_history()
1339
1286
  message_history_hashes = set([self.hash_message(m) for m in _message_history])
@@ -1841,49 +1788,14 @@ class BaseAgent(ABC):
1841
1788
 
1842
1789
  usage_limits = UsageLimits(request_limit=get_message_limit())
1843
1790
 
1844
- # Handle MCP servers - add them temporarily when using DBOS
1845
- if (
1846
- get_use_dbos()
1847
- and hasattr(self, "_mcp_servers")
1848
- and self._mcp_servers
1849
- ):
1850
- # Temporarily add MCP servers to the DBOS agent using internal _toolsets
1851
- original_toolsets = pydantic_agent._toolsets
1852
- pydantic_agent._toolsets = original_toolsets + self._mcp_servers
1853
- pydantic_agent._toolsets = original_toolsets + self._mcp_servers
1854
-
1855
- try:
1856
- # Set the workflow ID for DBOS context so DBOS and Code Puppy ID match
1857
- with SetWorkflowID(group_id):
1858
- result_ = await pydantic_agent.run(
1859
- prompt_payload,
1860
- message_history=self.get_message_history(),
1861
- usage_limits=usage_limits,
1862
- event_stream_handler=self._event_stream_handler,
1863
- **kwargs,
1864
- )
1865
- finally:
1866
- # Always restore original toolsets
1867
- pydantic_agent._toolsets = original_toolsets
1868
- elif get_use_dbos():
1869
- # DBOS without MCP servers
1870
- with SetWorkflowID(group_id):
1871
- result_ = await pydantic_agent.run(
1872
- prompt_payload,
1873
- message_history=self.get_message_history(),
1874
- usage_limits=usage_limits,
1875
- event_stream_handler=self._event_stream_handler,
1876
- **kwargs,
1877
- )
1878
- else:
1879
- # Non-DBOS path (MCP servers are already included)
1880
- result_ = await pydantic_agent.run(
1881
- prompt_payload,
1882
- message_history=self.get_message_history(),
1883
- usage_limits=usage_limits,
1884
- event_stream_handler=self._event_stream_handler,
1885
- **kwargs,
1886
- )
1791
+ # MCP servers are already included in the agent
1792
+ result_ = await pydantic_agent.run(
1793
+ prompt_payload,
1794
+ message_history=self.get_message_history(),
1795
+ usage_limits=usage_limits,
1796
+ event_stream_handler=self._event_stream_handler,
1797
+ **kwargs,
1798
+ )
1887
1799
  return result_
1888
1800
  except* UsageLimitExceeded as ule:
1889
1801
  emit_info(f"Usage limit exceeded: {str(ule)}", group_id=group_id)
@@ -1899,12 +1811,8 @@ class BaseAgent(ABC):
1899
1811
  )
1900
1812
  except* asyncio.exceptions.CancelledError:
1901
1813
  emit_info("Cancelled")
1902
- if get_use_dbos():
1903
- await DBOS.cancel_workflow_async(group_id)
1904
1814
  except* InterruptedError as ie:
1905
1815
  emit_info(f"Interrupted: {str(ie)}")
1906
- if get_use_dbos():
1907
- await DBOS.cancel_workflow_async(group_id)
1908
1816
  except* Exception as other_error:
1909
1817
  # Filter out CancelledError and UsageLimitExceeded from the exception group - let it propagate
1910
1818
  remaining_exceptions = []
@@ -12,11 +12,9 @@ import argparse
12
12
  import asyncio
13
13
  import os
14
14
  import sys
15
- import time
16
15
  import traceback
17
16
  from pathlib import Path
18
17
 
19
- from dbos import DBOS, DBOSConfig
20
18
  from rich.console import Console
21
19
 
22
20
  from code_puppy import __version__, callbacks, plugins
@@ -26,10 +24,8 @@ from code_puppy.command_line.clipboard import get_clipboard_manager
26
24
  from code_puppy.config import (
27
25
  AUTOSAVE_DIR,
28
26
  COMMAND_HISTORY_FILE,
29
- DBOS_DATABASE_URL,
30
27
  ensure_config_exists,
31
28
  finalize_autosave_session,
32
- get_use_dbos,
33
29
  initialize_command_history_file,
34
30
  save_command_to_history,
35
31
  )
@@ -287,33 +283,6 @@ async def main():
287
283
 
288
284
  await callbacks.on_startup()
289
285
 
290
- # Initialize DBOS if not disabled
291
- if get_use_dbos():
292
- # Append a Unix timestamp in ms to the version for uniqueness
293
- dbos_app_version = os.environ.get(
294
- "DBOS_APP_VERSION", f"{current_version}-{int(time.time() * 1000)}"
295
- )
296
- dbos_config: DBOSConfig = {
297
- "name": "dbos-code-puppy",
298
- "system_database_url": DBOS_DATABASE_URL,
299
- "run_admin_server": False,
300
- "conductor_key": os.environ.get(
301
- "DBOS_CONDUCTOR_KEY"
302
- ), # Optional, if set in env, connect to conductor
303
- "log_level": os.environ.get(
304
- "DBOS_LOG_LEVEL", "ERROR"
305
- ), # Default to ERROR level to suppress verbose logs
306
- "application_version": dbos_app_version, # Match DBOS app version to Code Puppy version
307
- }
308
- try:
309
- DBOS(config=dbos_config)
310
- DBOS.launch()
311
- except Exception as e:
312
- emit_error(f"Error initializing DBOS: {e}")
313
- sys.exit(1)
314
- else:
315
- pass
316
-
317
286
  global shutdown_flag
318
287
  shutdown_flag = False
319
288
  try:
@@ -338,8 +307,6 @@ async def main():
338
307
  if bus_renderer:
339
308
  bus_renderer.stop()
340
309
  await callbacks.on_shutdown()
341
- if get_use_dbos():
342
- DBOS.destroy()
343
310
 
344
311
 
345
312
  async def interactive_mode(message_renderer, initial_command: str = None) -> None:
@@ -907,8 +874,6 @@ def main_entry():
907
874
  except KeyboardInterrupt:
908
875
  # Note: Using sys.stderr for crash output - messaging system may not be available
909
876
  sys.stderr.write(traceback.format_exc())
910
- if get_use_dbos():
911
- DBOS.destroy()
912
877
  return 0
913
878
  finally:
914
879
  # Reset terminal on Unix-like systems (not Windows)
@@ -17,6 +17,7 @@ from prompt_toolkit.layout import Dimension, Layout, VSplit, Window
17
17
  from prompt_toolkit.layout.controls import FormattedTextControl
18
18
  from prompt_toolkit.widgets import Frame
19
19
 
20
+ from code_puppy.command_line.utils import safe_input
20
21
  from code_puppy.config import EXTRA_MODELS_FILE, set_config_value
21
22
  from code_puppy.messaging import emit_error, emit_info, emit_warning
22
23
  from code_puppy.models_dev_parser import ModelInfo, ModelsDevRegistry, ProviderInfo
@@ -724,8 +725,8 @@ class AddModelMenu:
724
725
  emit_info(f" {hint}")
725
726
 
726
727
  try:
727
- # Use regular input - simpler and works in threaded context
728
- value = input(f" Enter {env_var} (or press Enter to skip): ").strip()
728
+ # Use safe_input for cross-platform compatibility (Windows fix)
729
+ value = safe_input(f" Enter {env_var} (or press Enter to skip): ")
729
730
 
730
731
  if not value:
731
732
  emit_warning(
@@ -785,7 +786,7 @@ class AddModelMenu:
785
786
  )
786
787
 
787
788
  try:
788
- model_name = input(" Model ID: ").strip()
789
+ model_name = safe_input(" Model ID: ")
789
790
 
790
791
  if not model_name:
791
792
  emit_warning("No model name provided, cancelled.")
@@ -795,7 +796,7 @@ class AddModelMenu:
795
796
  emit_info("\n Enter the context window size (in tokens).")
796
797
  emit_info(" Common sizes: 8192, 32768, 128000, 200000, 1000000\n")
797
798
 
798
- context_input = input(" Context size [128000]: ").strip()
799
+ context_input = safe_input(" Context size [128000]: ")
799
800
 
800
801
  if not context_input:
801
802
  context_length = 128000 # Default
@@ -1045,11 +1046,9 @@ class AddModelMenu:
1045
1046
  f" It will be very limited for coding tasks."
1046
1047
  )
1047
1048
  try:
1048
- confirm = (
1049
- input("\n Are you sure you want to add this model? (y/N): ")
1050
- .strip()
1051
- .lower()
1052
- )
1049
+ confirm = safe_input(
1050
+ "\n Are you sure you want to add this model? (y/N): "
1051
+ ).lower()
1053
1052
  if confirm not in ("y", "yes"):
1054
1053
  emit_info("Model addition cancelled.")
1055
1054
  return False
@@ -43,7 +43,6 @@ def handle_show_command(command: str) -> bool:
43
43
  get_protected_token_count,
44
44
  get_puppy_name,
45
45
  get_temperature,
46
- get_use_dbos,
47
46
  get_yolo_mode,
48
47
  )
49
48
  from code_puppy.keymap import get_cancel_agent_display_name
@@ -72,7 +71,6 @@ def handle_show_command(command: str) -> bool:
72
71
  [bold]default_agent:[/bold] [cyan]{default_agent}[/cyan]
73
72
  [bold]model:[/bold] [green]{model}[/green]
74
73
  [bold]YOLO_MODE:[/bold] {"[red]ON[/red]" if yolo_mode else "[yellow]off[/yellow]"}
75
- [bold]DBOS:[/bold] {"[green]enabled[/green]" if get_use_dbos() else "[yellow]disabled[/yellow]"} (toggle: /set enable_dbos true|false)
76
74
  [bold]auto_save_session:[/bold] {"[green]enabled[/green]" if auto_save else "[yellow]disabled[/yellow]"}
77
75
  [bold]protected_tokens:[/bold] [cyan]{protected_tokens:,}[/cyan] recent tokens preserved
78
76
  [bold]compaction_threshold:[/bold] [cyan]{compaction_threshold:.1%}[/cyan] context usage triggers compaction
@@ -213,14 +211,6 @@ def handle_set_command(command: str) -> bool:
213
211
  )
214
212
  return True
215
213
  if key:
216
- # Check if we're toggling DBOS enablement
217
- if key == "enable_dbos":
218
- emit_info(
219
- Text.from_markup(
220
- "[yellow]⚠️ DBOS configuration changed. Please restart Code Puppy for this change to take effect.[/yellow]"
221
- )
222
- )
223
-
224
214
  # Validate cancel_agent_key before setting
225
215
  if key == "cancel_agent_key":
226
216
  from code_puppy.keymap import VALID_CANCEL_KEYS
@@ -7,6 +7,7 @@ MCP servers from the catalog.
7
7
  import os
8
8
  from typing import Dict, Optional
9
9
 
10
+ from code_puppy.command_line.utils import safe_input
10
11
  from code_puppy.messaging import emit_info, emit_success, emit_warning
11
12
 
12
13
  # Helpful hints for common environment variables
@@ -52,7 +53,7 @@ def prompt_for_server_config(manager, server) -> Optional[Dict]:
52
53
  # Get custom name
53
54
  default_name = server.name
54
55
  try:
55
- name_input = input(f" Server name [{default_name}]: ").strip()
56
+ name_input = safe_input(f" Server name [{default_name}]: ")
56
57
  server_name = name_input if name_input else default_name
57
58
  except (KeyboardInterrupt, EOFError):
58
59
  emit_info("")
@@ -63,9 +64,7 @@ def prompt_for_server_config(manager, server) -> Optional[Dict]:
63
64
  existing = find_server_id_by_name(manager, server_name)
64
65
  if existing:
65
66
  try:
66
- override = input(
67
- f" Server '{server_name}' exists. Override? [y/N]: "
68
- ).strip()
67
+ override = safe_input(f" Server '{server_name}' exists. Override? [y/N]: ")
69
68
  if not override.lower().startswith("y"):
70
69
  emit_warning("Installation cancelled")
71
70
  return None
@@ -91,7 +90,7 @@ def prompt_for_server_config(manager, server) -> Optional[Dict]:
91
90
  hint = get_env_var_hint(var)
92
91
  if hint:
93
92
  emit_info(f" {hint}")
94
- value = input(f" Enter {var}: ").strip()
93
+ value = safe_input(f" Enter {var}: ")
95
94
  if value:
96
95
  env_vars[var] = value
97
96
  # Save to config for future use
@@ -119,7 +118,7 @@ def prompt_for_server_config(manager, server) -> Optional[Dict]:
119
118
  prompt_str += " (optional)"
120
119
 
121
120
  try:
122
- value = input(f"{prompt_str}: ").strip()
121
+ value = safe_input(f"{prompt_str}: ")
123
122
  if value:
124
123
  cmd_args[name] = value
125
124
  elif default:
@@ -43,7 +43,7 @@ CUSTOM_SERVER_EXAMPLES = {
43
43
  "type": "http",
44
44
  "url": "http://localhost:8080/mcp",
45
45
  "headers": {
46
- "Authorization": "Bearer YOUR_API_KEY",
46
+ "Authorization": "Bearer $MY_API_KEY",
47
47
  "Content-Type": "application/json"
48
48
  },
49
49
  "timeout": 30
@@ -52,7 +52,7 @@ CUSTOM_SERVER_EXAMPLES = {
52
52
  "type": "sse",
53
53
  "url": "http://localhost:8080/sse",
54
54
  "headers": {
55
- "Authorization": "Bearer YOUR_API_KEY"
55
+ "Authorization": "Bearer $MY_API_KEY"
56
56
  }
57
57
  }""",
58
58
  }
@@ -367,24 +367,59 @@ class CustomServerForm:
367
367
  config_dict = json.loads(self.json_config)
368
368
 
369
369
  try:
370
- server_config = ServerConfig(
371
- id=server_name,
372
- name=server_name,
373
- type=server_type,
374
- enabled=True,
375
- config=config_dict,
376
- )
377
-
378
- # Register with manager
379
- server_id = self.manager.register_server(server_config)
380
-
381
- if not server_id:
382
- self.validation_error = "Failed to register server"
383
- self.status_message = (
384
- "Save failed: Could not register server (name may already exist)"
370
+ # In edit mode, find the existing server and update it
371
+ if self.edit_mode and self.original_name:
372
+ existing_config = self.manager.get_server_by_name(self.original_name)
373
+ if existing_config:
374
+ # Use the existing server's ID for the update
375
+ server_config = ServerConfig(
376
+ id=existing_config.id,
377
+ name=server_name,
378
+ type=server_type,
379
+ enabled=True,
380
+ config=config_dict,
381
+ )
382
+
383
+ # Update the server in the manager
384
+ success = self.manager.update_server(
385
+ existing_config.id, server_config
386
+ )
387
+
388
+ if not success:
389
+ self.validation_error = "Failed to update server"
390
+ self.status_message = "Save failed: Could not update server"
391
+ self.status_is_error = True
392
+ return False
393
+
394
+ server_id = existing_config.id
395
+ else:
396
+ # Original server not found, treat as new registration
397
+ server_config = ServerConfig(
398
+ id=server_name,
399
+ name=server_name,
400
+ type=server_type,
401
+ enabled=True,
402
+ config=config_dict,
403
+ )
404
+ server_id = self.manager.register_server(server_config)
405
+ else:
406
+ # New server - register it
407
+ server_config = ServerConfig(
408
+ id=server_name,
409
+ name=server_name,
410
+ type=server_type,
411
+ enabled=True,
412
+ config=config_dict,
385
413
  )
386
- self.status_is_error = True
387
- return False
414
+
415
+ # Register with manager
416
+ server_id = self.manager.register_server(server_config)
417
+
418
+ if not server_id:
419
+ self.validation_error = "Failed to register server"
420
+ self.status_message = "Save failed: Could not register server (name may already exist)"
421
+ self.status_is_error = True
422
+ return False
388
423
 
389
424
  # Save to mcp_servers.json for persistence
390
425
  if os.path.exists(MCP_SERVERS_FILE):
@@ -7,6 +7,7 @@ custom MCP servers with JSON configuration.
7
7
  import json
8
8
  import os
9
9
 
10
+ from code_puppy.command_line.utils import safe_input
10
11
  from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
11
12
 
12
13
  # Example configurations for each server type
@@ -24,7 +25,7 @@ CUSTOM_SERVER_EXAMPLES = {
24
25
  "type": "http",
25
26
  "url": "http://localhost:8080/mcp",
26
27
  "headers": {
27
- "Authorization": "Bearer YOUR_API_KEY",
28
+ "Authorization": "Bearer $MY_API_KEY",
28
29
  "Content-Type": "application/json"
29
30
  },
30
31
  "timeout": 30
@@ -33,7 +34,7 @@ CUSTOM_SERVER_EXAMPLES = {
33
34
  "type": "sse",
34
35
  "url": "http://localhost:8080/sse",
35
36
  "headers": {
36
- "Authorization": "Bearer YOUR_API_KEY"
37
+ "Authorization": "Bearer $MY_API_KEY"
37
38
  }
38
39
  }""",
39
40
  }
@@ -58,7 +59,7 @@ def prompt_and_install_custom_server(manager) -> bool:
58
59
 
59
60
  # Get server name
60
61
  try:
61
- server_name = input(" Server name: ").strip()
62
+ server_name = safe_input(" Server name: ")
62
63
  if not server_name:
63
64
  emit_warning("Server name is required")
64
65
  return False
@@ -71,9 +72,7 @@ def prompt_and_install_custom_server(manager) -> bool:
71
72
  existing = find_server_id_by_name(manager, server_name)
72
73
  if existing:
73
74
  try:
74
- override = input(
75
- f" Server '{server_name}' exists. Override? [y/N]: "
76
- ).strip()
75
+ override = safe_input(f" Server '{server_name}' exists. Override? [y/N]: ")
77
76
  if not override.lower().startswith("y"):
78
77
  emit_warning("Cancelled")
79
78
  return False
@@ -89,7 +88,7 @@ def prompt_and_install_custom_server(manager) -> bool:
89
88
  emit_info(" 3. 📡 sse - Server-Sent Events\n")
90
89
 
91
90
  try:
92
- type_choice = input(" Enter choice [1-3]: ").strip()
91
+ type_choice = safe_input(" Enter choice [1-3]: ")
93
92
  except (KeyboardInterrupt, EOFError):
94
93
  emit_info("")
95
94
  emit_warning("Cancelled")
@@ -115,8 +114,8 @@ def prompt_and_install_custom_server(manager) -> bool:
115
114
  empty_count = 0
116
115
  try:
117
116
  while True:
118
- line = input()
119
- if line.strip() == "":
117
+ line = safe_input("")
118
+ if line == "":
120
119
  empty_count += 1
121
120
  if empty_count >= 2:
122
121
  break