code-puppy 0.0.960__tar.gz → 0.0.962__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 (347) hide show
  1. {code_puppy-0.0.960 → code_puppy-0.0.962}/PKG-INFO +4 -3
  2. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/install_menu.py +6 -6
  3. code_puppy-0.0.962/code_puppy/early_startup.py +93 -0
  4. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/locales/en-US.json +52 -0
  5. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/locales/es.json +52 -0
  6. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/locales/fr-CA.json +52 -0
  7. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/main.py +8 -0
  8. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/blocking_startup.py +13 -98
  9. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/menu_lifecycle.py +0 -1
  10. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_scripts.py +81 -17
  11. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_workflows.py +18 -11
  12. {code_puppy-0.0.960 → code_puppy-0.0.962}/pyproject.toml +7 -3
  13. {code_puppy-0.0.960 → code_puppy-0.0.962}/.gitignore +0 -0
  14. {code_puppy-0.0.960 → code_puppy-0.0.962}/LICENSE +0 -0
  15. {code_puppy-0.0.960 → code_puppy-0.0.962}/README.md +0 -0
  16. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/__init__.py +0 -0
  17. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/__main__.py +0 -0
  18. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/_plugin_i18n_lifecycle.py +0 -0
  19. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/_pydantic_tool_helpers.py +0 -0
  20. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/_pydantic_video_patch.py +0 -0
  21. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agent_completion_inbox.py +0 -0
  22. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agent_execution_context.py +0 -0
  23. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/__init__.py +0 -0
  24. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_builder.py +0 -0
  25. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_code_mode.py +0 -0
  26. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_code_mode_guidance.py +0 -0
  27. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_code_mode_session.py +0 -0
  28. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_compaction.py +0 -0
  29. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_diagnostics.py +0 -0
  30. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_foreign_thinking.py +0 -0
  31. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_history.py +0 -0
  32. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_key_listeners.py +0 -0
  33. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_mcp.py +0 -0
  34. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_model_message_transform.py +0 -0
  35. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_multimodal_error_hint.py +0 -0
  36. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_non_streaming_render.py +0 -0
  37. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_output_limits.py +0 -0
  38. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_rules.py +0 -0
  39. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_run_signals.py +0 -0
  40. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_runtime.py +0 -0
  41. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_steer_processor.py +0 -0
  42. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_subagent_recursion.py +0 -0
  43. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_token_limit_hint.py +0 -0
  44. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_tty_modes.py +0 -0
  45. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_windows_console.py +0 -0
  46. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/_wire_tool_names.py +0 -0
  47. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/agent_code_puppy.py +0 -0
  48. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/agent_creator_agent.py +0 -0
  49. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/agent_helios.py +0 -0
  50. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/agent_manager.py +0 -0
  51. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/agent_model_judge.py +0 -0
  52. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/agent_planning.py +0 -0
  53. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/agent_qa_kitten.py +0 -0
  54. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/agent_web_retriever.py +0 -0
  55. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/base_agent.py +0 -0
  56. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/event_stream_handler.py +0 -0
  57. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/json_agent.py +0 -0
  58. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/retry_checkpoint.py +0 -0
  59. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/retry_profiles.py +0 -0
  60. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/run_invocation.py +0 -0
  61. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/run_stats.py +0 -0
  62. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/smooth_stream.py +0 -0
  63. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/stream_status.py +0 -0
  64. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/subagent_stream_handler.py +0 -0
  65. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/agents/thinking_output.py +0 -0
  66. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/asyncio_cleanup.py +0 -0
  67. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/atomic_io.py +0 -0
  68. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/atomic_json.py +0 -0
  69. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/ca_bundle.py +0 -0
  70. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/callbacks.py +0 -0
  71. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/capabilities/__init__.py +0 -0
  72. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/capabilities/compaction.py +0 -0
  73. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/capabilities/eager_timing.py +0 -0
  74. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/chatgpt_codex_client.py +0 -0
  75. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/claude_cache_client.py +0 -0
  76. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/claude_oauth_transport.py +0 -0
  77. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/claude_thinking_display.py +0 -0
  78. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/cli_runner.py +0 -0
  79. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/__init__.py +0 -0
  80. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/add_model_details.py +0 -0
  81. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/add_model_menu.py +0 -0
  82. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/add_model_menus.py +0 -0
  83. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/add_model_vllm.py +0 -0
  84. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/agent_menu.py +0 -0
  85. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/attachments.py +0 -0
  86. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/autosave_menu.py +0 -0
  87. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/autosave_search.py +0 -0
  88. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/clipboard.py +0 -0
  89. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/colors_menu.py +0 -0
  90. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/command_handler.py +0 -0
  91. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/command_registry.py +0 -0
  92. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/completers.py +0 -0
  93. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/completion_cache.py +0 -0
  94. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/config_apply.py +0 -0
  95. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/config_commands.py +0 -0
  96. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/core_commands.py +0 -0
  97. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/file_index.py +0 -0
  98. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/file_path_completion.py +0 -0
  99. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/help_catalog.py +0 -0
  100. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/help_overlay.py +0 -0
  101. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/image_utils.py +0 -0
  102. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/load_context_completion.py +0 -0
  103. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/__init__.py +0 -0
  104. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/base.py +0 -0
  105. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
  106. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
  107. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
  108. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/edit_command.py +0 -0
  109. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/handler.py +0 -0
  110. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/help_command.py +0 -0
  111. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/install_command.py +0 -0
  112. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/list_command.py +0 -0
  113. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/logs_command.py +0 -0
  114. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/mcp_servers_store.py +0 -0
  115. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/remove_command.py +0 -0
  116. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/restart_command.py +0 -0
  117. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/search_command.py +0 -0
  118. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/silence_warning_command.py +0 -0
  119. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/start_all_command.py +0 -0
  120. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/start_command.py +0 -0
  121. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/status_command.py +0 -0
  122. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
  123. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/stop_command.py +0 -0
  124. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/trust_command.py +0 -0
  125. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/utils.py +0 -0
  126. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
  127. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp_binding_menu.py +0 -0
  128. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/mcp_completion.py +0 -0
  129. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/menu_session.py +0 -0
  130. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/model_picker_completion.py +0 -0
  131. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/model_settings_defs.py +0 -0
  132. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/model_settings_menu.py +0 -0
  133. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/onboarding_slides.py +0 -0
  134. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/onboarding_wizard.py +0 -0
  135. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/pagination.py +0 -0
  136. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/pin_command_completion.py +0 -0
  137. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/refresh_models.py +0 -0
  138. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/session_browser.py +0 -0
  139. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/session_browser_data.py +0 -0
  140. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/session_commands.py +0 -0
  141. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/set_menu.py +0 -0
  142. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/set_menu_catalog.py +0 -0
  143. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/set_menu_schema.py +0 -0
  144. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/set_menu_settings.py +0 -0
  145. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/set_menu_shims.py +0 -0
  146. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/set_menu_values.py +0 -0
  147. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/shell_passthrough.py +0 -0
  148. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/skills_completion.py +0 -0
  149. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/tui_style.py +0 -0
  150. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/uc_menu.py +0 -0
  151. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/command_line/utils.py +0 -0
  152. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/config.py +0 -0
  153. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/config_file.py +0 -0
  154. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/context_accounting.py +0 -0
  155. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/diagnostics.py +0 -0
  156. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/error_format.py +0 -0
  157. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/error_hooks.py +0 -0
  158. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/error_logging.py +0 -0
  159. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/error_policy.py +0 -0
  160. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/error_redaction.py +0 -0
  161. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/events/__init__.py +0 -0
  162. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/events/bridge.py +0 -0
  163. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/file_completion_io.py +0 -0
  164. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/file_completion_tokens.py +0 -0
  165. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/gemini_code_assist.py +0 -0
  166. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/gemini_common.py +0 -0
  167. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/gemini_model.py +0 -0
  168. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/README.md +0 -0
  169. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/__init__.py +0 -0
  170. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/aliases.py +0 -0
  171. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/engine.py +0 -0
  172. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/executor.py +0 -0
  173. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/matcher.py +0 -0
  174. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/models.py +0 -0
  175. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/registry.py +0 -0
  176. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/hook_engine/validator.py +0 -0
  177. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/http_retry.py +0 -0
  178. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/http_utils.py +0 -0
  179. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/httpx2_utils.py +0 -0
  180. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/__init__.py +0 -0
  181. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/audit.py +0 -0
  182. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/catalog.py +0 -0
  183. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/formats.py +0 -0
  184. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/interpolation.py +0 -0
  185. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/locale.py +0 -0
  186. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/plugin_catalog.py +0 -0
  187. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/plurals.py +0 -0
  188. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/pseudo.py +0 -0
  189. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/i18n/translate.py +0 -0
  190. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/kennel_provider.py +0 -0
  191. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/keymap.py +0 -0
  192. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/list_filtering.py +0 -0
  193. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/malloc_noise_guard.py +0 -0
  194. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/__init__.py +0 -0
  195. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/agent_bindings.py +0 -0
  196. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/async_lifecycle.py +0 -0
  197. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/circuit_breaker.py +0 -0
  198. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/config_wizard.py +0 -0
  199. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/dashboard.py +0 -0
  200. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/error_isolation.py +0 -0
  201. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/examples/retry_example.py +0 -0
  202. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/health_monitor.py +0 -0
  203. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/http_auth.py +0 -0
  204. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/managed_server.py +0 -0
  205. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/manager.py +0 -0
  206. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/manager_lifecycle.py +0 -0
  207. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/mcp_logs.py +0 -0
  208. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/project_config.py +0 -0
  209. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/registry.py +0 -0
  210. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/retry_manager.py +0 -0
  211. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/server_registry_catalog.py +0 -0
  212. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/status_tracker.py +0 -0
  213. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/system_tools.py +0 -0
  214. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/tool_arg_coercion.py +0 -0
  215. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_/toolset_utils.py +0 -0
  216. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_prompts/__init__.py +0 -0
  217. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/mcp_prompts/hook_creator.py +0 -0
  218. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/__init__.py +0 -0
  219. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/bar_painters.py +0 -0
  220. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/bar_rendering.py +0 -0
  221. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/bottom_bar.py +0 -0
  222. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/bus.py +0 -0
  223. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/chords.py +0 -0
  224. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/commands.py +0 -0
  225. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_actions.py +0 -0
  226. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_completion.py +0 -0
  227. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_display.py +0 -0
  228. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_history.py +0 -0
  229. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_keys.py +0 -0
  230. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_paste.py +0 -0
  231. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_queue.py +0 -0
  232. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_submission.py +0 -0
  233. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/editor_undo.py +0 -0
  234. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/external_editor.py +0 -0
  235. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/identity_line.py +0 -0
  236. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/idle_status.py +0 -0
  237. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/inline_bar.py +0 -0
  238. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/line_editor.py +0 -0
  239. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/markdown_patches.py +0 -0
  240. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/message_queue.py +0 -0
  241. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/messages.py +0 -0
  242. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/pause_controller.py +0 -0
  243. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/prompt_prefix_style.py +0 -0
  244. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/queue_console.py +0 -0
  245. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/renderers.py +0 -0
  246. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/rich_renderer.py +0 -0
  247. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/run_ui.py +0 -0
  248. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/run_ui_wiring.py +0 -0
  249. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/safe_console.py +0 -0
  250. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/safe_stream.py +0 -0
  251. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/speculation_line.py +0 -0
  252. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/speculation_stats.py +0 -0
  253. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/speculation_toggle.py +0 -0
  254. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/spinner/__init__.py +0 -0
  255. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/status_line.py +0 -0
  256. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/subagent_console.py +0 -0
  257. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/theme_accent.py +0 -0
  258. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/tool_output.py +0 -0
  259. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/messaging/transcript_guard.py +0 -0
  260. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/model_descriptions.py +0 -0
  261. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/model_factory.py +0 -0
  262. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/model_switching.py +0 -0
  263. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/model_utils.py +0 -0
  264. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/models.json +0 -0
  265. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/models_dev_api.json +0 -0
  266. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/models_dev_family.py +0 -0
  267. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/models_dev_parser.py +0 -0
  268. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/observability.py +0 -0
  269. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/platform_utils.py +0 -0
  270. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/__init__.py +0 -0
  271. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/config.py +0 -0
  272. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/oauth_pasteback.py +0 -0
  273. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/oauth_puppy_html.py +0 -0
  274. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/prewarm_sdks/__init__.py +0 -0
  275. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/prewarm_sdks/prewarm.py +0 -0
  276. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/prewarm_sdks/register_callbacks.py +0 -0
  277. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/trust.py +0 -0
  278. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/plugins/trust_notice.py +0 -0
  279. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/private_inference.py +0 -0
  280. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/provider_credentials.py +0 -0
  281. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/provider_identity.py +0 -0
  282. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/pydantic_patches.py +0 -0
  283. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/reopenable_async_client.py +0 -0
  284. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/round_robin_model.py +0 -0
  285. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/secret_store.py +0 -0
  286. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/secret_store_backends.py +0 -0
  287. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/session_format_migration.py +0 -0
  288. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/session_lifecycle.py +0 -0
  289. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/session_migration.py +0 -0
  290. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/session_storage.py +0 -0
  291. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/session_surrogate_unpickler.py +0 -0
  292. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/shared_credentials.py +0 -0
  293. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/skill_provider.py +0 -0
  294. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/splash.py +0 -0
  295. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/status_display.py +0 -0
  296. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/steer_metadata.py +0 -0
  297. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/terminal_utils.py +0 -0
  298. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/token_usage.py +0 -0
  299. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/__init__.py +0 -0
  300. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/_browser_registry.py +0 -0
  301. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/_lazy.py +0 -0
  302. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/agent_tools.py +0 -0
  303. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/approval_feedback.py +0 -0
  304. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/__init__.py +0 -0
  305. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/constants.py +0 -0
  306. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
  307. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/handler.py +0 -0
  308. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/models.py +0 -0
  309. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/registration.py +0 -0
  310. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/renderers.py +0 -0
  311. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
  312. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/theme.py +0 -0
  313. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
  314. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/__init__.py +0 -0
  315. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_control.py +0 -0
  316. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_interactions.py +0 -0
  317. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_locator_resolver.py +0 -0
  318. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_locators.py +0 -0
  319. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_manager.py +0 -0
  320. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_navigation.py +0 -0
  321. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_page_snapshot.py +0 -0
  322. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_screenshot.py +0 -0
  323. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/browser_semantic_interactions.py +0 -0
  324. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/browser/tool_registry.py +0 -0
  325. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/command_runner.py +0 -0
  326. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/common.py +0 -0
  327. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/display.py +0 -0
  328. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/file_modifications.py +0 -0
  329. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/file_operations.py +0 -0
  330. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/file_permission_state.py +0 -0
  331. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/fs_access.py +0 -0
  332. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/image_tools.py +0 -0
  333. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/io_backends.py +0 -0
  334. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/model_tools.py +0 -0
  335. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/ripgrep.py +0 -0
  336. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/shell_backgrounding.py +0 -0
  337. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/skills_tools.py +0 -0
  338. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/subagent_context.py +0 -0
  339. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/subagent_invocation.py +0 -0
  340. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/subagent_usage_metrics.py +0 -0
  341. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/tools_content.py +0 -0
  342. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/universal_constructor.py +0 -0
  343. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/tools/win_shell_io.py +0 -0
  344. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/undo_manager.py +0 -0
  345. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/universal_constructor_provider.py +0 -0
  346. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/version_checker.py +0 -0
  347. {code_puppy-0.0.960 → code_puppy-0.0.962}/code_puppy/zai_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: code-puppy
3
- Version: 0.0.960
3
+ Version: 0.0.962
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
@@ -20,7 +20,8 @@ Requires-Dist: anthropic<2,>=1.0.0
20
20
  Requires-Dist: anyio>=4.14.2
21
21
  Requires-Dist: azure-identity>=1.15.0
22
22
  Requires-Dist: boto3>=1.43.9
23
- Requires-Dist: code-puppy-core-plugins>=0.0.97
23
+ Requires-Dist: code-puppy-core-plugins>=0.0.103
24
+ Requires-Dist: cryptography<49; sys_platform == 'darwin' and platform_machine == 'x86_64'
24
25
  Requires-Dist: fastmcp-slim[client]<5,>=3.3.0
25
26
  Requires-Dist: httpx2>=2.7
26
27
  Requires-Dist: httpx[http2]>=0.24.1
@@ -47,7 +48,7 @@ Requires-Dist: websockets>=14.0
47
48
  Provides-Extra: bedrock
48
49
  Requires-Dist: boto3>=1.35.0; extra == 'bedrock'
49
50
  Provides-Extra: computer-use
50
- Requires-Dist: code-puppy-core-plugins[computer-use]>=0.0.97; extra == 'computer-use'
51
+ Requires-Dist: code-puppy-core-plugins[computer-use]>=0.0.103; extra == 'computer-use'
51
52
  Provides-Extra: durable
52
53
  Requires-Dist: dbos>=2.11.0; extra == 'durable'
53
54
  Description-Content-Type: text/markdown
@@ -11,7 +11,8 @@ import os
11
11
  import textwrap
12
12
  from typing import Callable, List
13
13
 
14
- from code_puppy.messaging import emit_error, emit_info, emit_warning
14
+ from code_puppy.i18n import t
15
+ from code_puppy.messaging import emit_error, emit_warning
15
16
  from code_puppy.tools.command_runner import set_awaiting_user_input
16
17
 
17
18
  from .catalog_server_installer import (
@@ -54,13 +55,13 @@ def load_catalog():
54
55
 
55
56
  categories = [CUSTOM_SERVER_CATEGORY] + catalog.list_categories()
56
57
  if len(categories) <= 1: # Only custom category
57
- emit_error("No categories found in server catalog")
58
+ emit_error(t("mcp.install_menu.no_categories"))
58
59
  return catalog, categories
59
60
  except ImportError as e:
60
- emit_error(f"Server catalog not available: {e}")
61
+ emit_error(t("mcp.install_menu.catalog_unavailable", error=e))
61
62
  return None, [CUSTOM_SERVER_CATEGORY]
62
63
  except Exception as e:
63
- emit_error(f"Error loading server catalog: {e}")
64
+ emit_error(t("mcp.install_menu.load_error", error=e))
64
65
  return None, [CUSTOM_SERVER_CATEGORY]
65
66
 
66
67
 
@@ -289,7 +290,7 @@ def run_mcp_install_menu(manager) -> bool:
289
290
 
290
291
  catalog, categories = load_catalog()
291
292
  if not categories:
292
- emit_warning("No MCP server catalog available.")
293
+ emit_warning(t("mcp.install_menu.no_catalog"))
293
294
  return False
294
295
 
295
296
  set_awaiting_user_input(True)
@@ -300,7 +301,6 @@ def run_mcp_install_menu(manager) -> bool:
300
301
  set_awaiting_user_input(False)
301
302
 
302
303
  if selection is None:
303
- emit_info("Exited MCP server browser")
304
304
  return False
305
305
 
306
306
  # Custom server: hand off to the form (it owns its own session).
@@ -0,0 +1,93 @@
1
+ """Pre-import startup hooks for in-tree plugins.
2
+
3
+ ``code_puppy/main.py`` calls :func:`run_early_startup` while the splash is on
4
+ screen and BEFORE the heavy ``cli_runner`` import (pydantic-ai, prompt_toolkit,
5
+ rich, ...). It exists for setup that has to happen before those imports, such
6
+ as sanitising proxy or certificate environment variables that would otherwise
7
+ break them.
8
+
9
+ A plugin opts in by shipping ``code_puppy/plugins/<name>/early_startup.py``
10
+ with a zero-argument ``run()``. The rules:
11
+
12
+ * Keep the hook, and the plugin's package ``__init__``, light. Everything here
13
+ runs before the app is ready, so stdlib-only is the goal.
14
+ * Only in-tree plugin directories are scanned. User and project plugins are
15
+ untrusted code and go through the trust-gated loader later, never from here.
16
+ * The plugin enable/disable list is not consulted (configuration is not loaded
17
+ yet), so a hook that needs a kill switch should read its own environment
18
+ variable.
19
+ * A hook that raises is skipped and recorded in ``errors.log`` via
20
+ :func:`report_failures`; startup continues. ``main.py`` calls it right after
21
+ the hooks and before the heavy import, so even a later import crash leaves
22
+ the hook failure on record. ``KeyboardInterrupt`` and ``SystemExit`` still
23
+ propagate.
24
+
25
+ This module is stdlib-only by design, like ``code_puppy.splash`` -- do not
26
+ import anything else from ``code_puppy`` here. The lazy
27
+ ``code_puppy.error_logging`` import inside :func:`report_failures` is the
28
+ deliberate exception; it only runs when a hook actually failed.
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import importlib
34
+ import logging
35
+ import os
36
+ from pathlib import Path
37
+
38
+ logger = logging.getLogger(__name__)
39
+
40
+ HOOK_FILE = "early_startup.py"
41
+ _PLUGINS_DIR = Path(__file__).resolve().parent / "plugins"
42
+ _PLUGINS_PACKAGE = "code_puppy.plugins"
43
+
44
+
45
+ def discover() -> list[str]:
46
+ """Names of in-tree plugins that ship an ``early_startup.py``, sorted."""
47
+ try:
48
+ with os.scandir(_PLUGINS_DIR) as entries:
49
+ return sorted(
50
+ entry.name
51
+ for entry in entries
52
+ if not entry.name.startswith(("_", "."))
53
+ and entry.is_dir()
54
+ and os.path.isfile(os.path.join(entry.path, HOOK_FILE))
55
+ )
56
+ except OSError:
57
+ return []
58
+
59
+
60
+ def run_early_startup() -> list[tuple[str, Exception]]:
61
+ """Run every discovered plugin's ``run()``, in name order. Never raises.
62
+
63
+ Returns ``(plugin name, exception)`` for each hook that failed, for
64
+ :func:`report_failures`.
65
+ """
66
+ failures: list[tuple[str, Exception]] = []
67
+ for name in discover():
68
+ try:
69
+ module = importlib.import_module(f"{_PLUGINS_PACKAGE}.{name}.early_startup")
70
+ module.run()
71
+ except Exception as exc:
72
+ logger.debug("early startup hook of plugin %r failed", name, exc_info=True)
73
+ failures.append((name, exc))
74
+ return failures
75
+
76
+
77
+ def report_failures(failures: list[tuple[str, Exception]]) -> None:
78
+ """Record failed hooks in ``errors.log``. Never raises."""
79
+ if not failures:
80
+ return
81
+ try:
82
+ from code_puppy.error_logging import log_error
83
+
84
+ for name, exc in failures:
85
+ log_error(
86
+ exc,
87
+ context=(
88
+ "code_puppy.early_startup::run_early_startup: "
89
+ f"early startup hook of plugin {name!r} failed"
90
+ ),
91
+ )
92
+ except Exception:
93
+ logger.debug("reporting early startup hook failures failed", exc_info=True)
@@ -53,6 +53,52 @@
53
53
  "ask_user_question.question.select_multiple": "(select multiple)",
54
54
  "ask_user_question.render.error": "[render error: {error_type}: {error}]",
55
55
  "ask_user_question.timeout.warning": "Timeout in {remaining}s - press any key to continue",
56
+ "browser.automation.clear_highlights": "BROWSER CLEAR HIGHLIGHTS 🧹",
57
+ "browser.automation.element_highlighted": "Highlighted element: {selector}",
58
+ "browser.automation.element_now_attached": "Element {selector} is now attached",
59
+ "browser.automation.element_now_detached": "Element {selector} is now detached",
60
+ "browser.automation.element_now_hidden": "Element {selector} is now hidden",
61
+ "browser.automation.element_now_visible": "Element {selector} is now visible",
62
+ "browser.automation.execute_js": "BROWSER EXECUTE JS 📜 {details}",
63
+ "browser.automation.highlight_element": "BROWSER HIGHLIGHT ELEMENT 🔦 {details}",
64
+ "browser.automation.highlights_cleared": {
65
+ "one": "Cleared {count} highlight",
66
+ "other": "Cleared {count} highlights"
67
+ },
68
+ "browser.automation.js_executed": "JavaScript executed successfully",
69
+ "browser.automation.js_failed": "JavaScript execution failed: {error}",
70
+ "browser.automation.list_workflows": "LIST WORKFLOWS",
71
+ "browser.automation.read_workflow": "READ WORKFLOW {details}",
72
+ "browser.automation.save_workflow": "SAVE WORKFLOW 💾 {details}",
73
+ "browser.automation.scroll": "BROWSER SCROLL {details}",
74
+ "browser.automation.scroll_direction_unknown": "Unknown scroll direction: '{direction}'",
75
+ "browser.automation.scroll_to_element": "BROWSER SCROLL TO ELEMENT 🎯 {details}",
76
+ "browser.automation.scrolled_element_down": "Scrolled element '{selector}' down",
77
+ "browser.automation.scrolled_element_left": "Scrolled element '{selector}' left",
78
+ "browser.automation.scrolled_element_right": "Scrolled element '{selector}' right",
79
+ "browser.automation.scrolled_element_up": "Scrolled element '{selector}' up",
80
+ "browser.automation.scrolled_page_down": "Scrolled page down",
81
+ "browser.automation.scrolled_page_left": "Scrolled page left",
82
+ "browser.automation.scrolled_page_right": "Scrolled page right",
83
+ "browser.automation.scrolled_page_up": "Scrolled page up",
84
+ "browser.automation.scrolled_to_element": "Scrolled to element: {selector}",
85
+ "browser.automation.set_viewport": "BROWSER SET VIEWPORT 🖥️ {details}",
86
+ "browser.automation.viewport_set": "Set viewport size to {size}",
87
+ "browser.automation.wait_for_element": "BROWSER WAIT FOR ELEMENT {details}",
88
+ "browser.automation.workflow_not_found": "Workflow not found: {name}",
89
+ "browser.automation.workflow_read": {
90
+ "one": "Workflow read successfully: {count} character",
91
+ "other": "Workflow read successfully: {count} characters"
92
+ },
93
+ "browser.automation.workflow_read_failed": "Failed to read workflow: {error}",
94
+ "browser.automation.workflow_save_failed": "Failed to save workflow: {error}",
95
+ "browser.automation.workflow_saved": "Workflow saved successfully: {path}",
96
+ "browser.automation.workflow_unreadable": "Could not read {path}: {error}",
97
+ "browser.automation.workflows_found": {
98
+ "one": "Found {count} workflow",
99
+ "other": "Found {count} workflows"
100
+ },
101
+ "browser.automation.workflows_list_failed": "Failed to list workflows: {error}",
56
102
  "browser.chromium.install_failed": "Chromium is missing and automatic installation failed (exit code {exit_code}). Run this command and retry:\n{command}\n\nInstaller output:\n{output}",
57
103
  "browser.chromium.install_no_output": "No installer output was produced.",
58
104
  "browser.chromium.install_start_failed": "Chromium is missing and its installer could not start: {error}\nRun this command and retry:\n{command}",
@@ -436,6 +482,10 @@
436
482
  "mcp.install.no_description": "No description available",
437
483
  "mcp.install.no_server_found": "No server found matching '{server}'",
438
484
  "mcp.install.registry_unavailable": "Server registry not available",
485
+ "mcp.install_menu.catalog_unavailable": "Server catalog not available: {error}",
486
+ "mcp.install_menu.load_error": "Error loading server catalog: {error}",
487
+ "mcp.install_menu.no_catalog": "No MCP server catalog available.",
488
+ "mcp.install_menu.no_categories": "No categories found in server catalog",
439
489
  "mcp.install_wizard.already_set": "Already set",
440
490
  "mcp.install_wizard.cancelled": "Installation cancelled",
441
491
  "mcp.install_wizard.catalog_unavailable": "Server catalog not available",
@@ -597,6 +647,8 @@
597
647
  "other": "✓ Also unbound from {count} agents: {agents}"
598
648
  },
599
649
  "mcp.server.usage": "Usage: /mcp {command} <server_name>",
650
+ "mcp.startup.not_started": "MCP server '{server}' didn't start. Run {logs_command} to investigate, or unbind it via {agents_command}.",
651
+ "mcp.startup.stderr_line": "MCP {server}: {line}",
600
652
  "mcp.state.error": "⚠ Err",
601
653
  "mcp.state.quarantined": "⏸ Quar",
602
654
  "mcp.state.running": "✓ Run",
@@ -53,6 +53,52 @@
53
53
  "ask_user_question.question.select_multiple": "(selección múltiple)",
54
54
  "ask_user_question.render.error": "[error de renderizado: {error_type}: {error}]",
55
55
  "ask_user_question.timeout.warning": "Tiempo de espera en {remaining}s - pulsa cualquier tecla para continuar",
56
+ "browser.automation.clear_highlights": "QUITAR RESALTADOS EN EL NAVEGADOR 🧹",
57
+ "browser.automation.element_highlighted": "Elemento resaltado: {selector}",
58
+ "browser.automation.element_now_attached": "El elemento {selector} ya está en el DOM",
59
+ "browser.automation.element_now_detached": "El elemento {selector} ya no está en el DOM",
60
+ "browser.automation.element_now_hidden": "El elemento {selector} ahora está oculto",
61
+ "browser.automation.element_now_visible": "El elemento {selector} ahora está visible",
62
+ "browser.automation.execute_js": "EJECUTAR JS EN EL NAVEGADOR 📜 {details}",
63
+ "browser.automation.highlight_element": "RESALTAR ELEMENTO EN EL NAVEGADOR 🔦 {details}",
64
+ "browser.automation.highlights_cleared": {
65
+ "one": "Se quitó {count} resaltado",
66
+ "other": "Se quitaron {count} resaltados"
67
+ },
68
+ "browser.automation.js_executed": "JavaScript se ejecutó correctamente",
69
+ "browser.automation.js_failed": "No se pudo ejecutar JavaScript: {error}",
70
+ "browser.automation.list_workflows": "LISTAR FLUJOS DE TRABAJO",
71
+ "browser.automation.read_workflow": "LEER FLUJO DE TRABAJO {details}",
72
+ "browser.automation.save_workflow": "GUARDAR FLUJO DE TRABAJO 💾 {details}",
73
+ "browser.automation.scroll": "DESPLAZAR EN EL NAVEGADOR {details}",
74
+ "browser.automation.scroll_direction_unknown": "Dirección de desplazamiento desconocida: '{direction}'",
75
+ "browser.automation.scroll_to_element": "DESPLAZAR HASTA EL ELEMENTO EN EL NAVEGADOR 🎯 {details}",
76
+ "browser.automation.scrolled_element_down": "Se desplazó el elemento '{selector}' hacia abajo",
77
+ "browser.automation.scrolled_element_left": "Se desplazó el elemento '{selector}' hacia la izquierda",
78
+ "browser.automation.scrolled_element_right": "Se desplazó el elemento '{selector}' hacia la derecha",
79
+ "browser.automation.scrolled_element_up": "Se desplazó el elemento '{selector}' hacia arriba",
80
+ "browser.automation.scrolled_page_down": "Se desplazó la página hacia abajo",
81
+ "browser.automation.scrolled_page_left": "Se desplazó la página hacia la izquierda",
82
+ "browser.automation.scrolled_page_right": "Se desplazó la página hacia la derecha",
83
+ "browser.automation.scrolled_page_up": "Se desplazó la página hacia arriba",
84
+ "browser.automation.scrolled_to_element": "Se desplazó hasta el elemento: {selector}",
85
+ "browser.automation.set_viewport": "AJUSTAR VENTANA GRÁFICA DEL NAVEGADOR 🖥️ {details}",
86
+ "browser.automation.viewport_set": "Tamaño de la ventana gráfica establecido en {size}",
87
+ "browser.automation.wait_for_element": "ESPERAR ELEMENTO EN EL NAVEGADOR {details}",
88
+ "browser.automation.workflow_not_found": "Flujo de trabajo no encontrado: {name}",
89
+ "browser.automation.workflow_read": {
90
+ "one": "Flujo de trabajo leído correctamente: {count} carácter",
91
+ "other": "Flujo de trabajo leído correctamente: {count} caracteres"
92
+ },
93
+ "browser.automation.workflow_read_failed": "No se pudo leer el flujo de trabajo: {error}",
94
+ "browser.automation.workflow_save_failed": "No se pudo guardar el flujo de trabajo: {error}",
95
+ "browser.automation.workflow_saved": "Flujo de trabajo guardado correctamente: {path}",
96
+ "browser.automation.workflow_unreadable": "No se pudo leer {path}: {error}",
97
+ "browser.automation.workflows_found": {
98
+ "one": "Se encontró {count} flujo de trabajo",
99
+ "other": "Se encontraron {count} flujos de trabajo"
100
+ },
101
+ "browser.automation.workflows_list_failed": "No se pudieron listar los flujos de trabajo: {error}",
56
102
  "browser.chromium.install_failed": "Chromium no está instalado y la instalación automática falló (código de salida {exit_code}). Ejecuta este comando y vuelve a intentarlo:\n{command}\n\nSalida del instalador:\n{output}",
57
103
  "browser.chromium.install_no_output": "El instalador no produjo ninguna salida.",
58
104
  "browser.chromium.install_start_failed": "Chromium no está instalado y no se pudo iniciar su instalador: {error}\nEjecuta este comando y vuelve a intentarlo:\n{command}",
@@ -436,6 +482,10 @@
436
482
  "mcp.install.no_description": "No hay descripción disponible",
437
483
  "mcp.install.no_server_found": "No se encontró ningún servidor que coincida con '{server}'",
438
484
  "mcp.install.registry_unavailable": "Registro de servidores no disponible",
485
+ "mcp.install_menu.catalog_unavailable": "El catálogo de servidores no está disponible: {error}",
486
+ "mcp.install_menu.load_error": "Error al cargar el catálogo de servidores: {error}",
487
+ "mcp.install_menu.no_catalog": "No hay ningún catálogo de servidores MCP disponible.",
488
+ "mcp.install_menu.no_categories": "No se encontraron categorías en el catálogo de servidores",
439
489
  "mcp.install_wizard.already_set": "Ya configurada",
440
490
  "mcp.install_wizard.cancelled": "Instalación cancelada",
441
491
  "mcp.install_wizard.catalog_unavailable": "El catálogo de servidores no está disponible",
@@ -597,6 +647,8 @@
597
647
  "other": "✓ También se desvinculó de {count} agentes: {agents}"
598
648
  },
599
649
  "mcp.server.usage": "Uso: /mcp {command} <server_name>",
650
+ "mcp.startup.not_started": "El servidor MCP '{server}' no se inició. Ejecuta {logs_command} para investigar o desvincúlalo con {agents_command}.",
651
+ "mcp.startup.stderr_line": "MCP {server}: {line}",
600
652
  "mcp.state.error": "⚠ Error",
601
653
  "mcp.state.quarantined": "⏸ Cuarentena",
602
654
  "mcp.state.running": "✓ Activo",
@@ -53,6 +53,52 @@
53
53
  "ask_user_question.question.select_multiple": "(sélection multiple)",
54
54
  "ask_user_question.render.error": "[erreur de rendu : {error_type} : {error}]",
55
55
  "ask_user_question.timeout.warning": "Délai dans {remaining}s - appuyez sur une touche pour continuer",
56
+ "browser.automation.clear_highlights": "EFFACER LES SURLIGNAGES DANS LE NAVIGATEUR 🧹",
57
+ "browser.automation.element_highlighted": "Élément surligné : {selector}",
58
+ "browser.automation.element_now_attached": "L’élément {selector} est maintenant dans le DOM",
59
+ "browser.automation.element_now_detached": "L’élément {selector} n’est plus dans le DOM",
60
+ "browser.automation.element_now_hidden": "L’élément {selector} est maintenant masqué",
61
+ "browser.automation.element_now_visible": "L’élément {selector} est maintenant visible",
62
+ "browser.automation.execute_js": "EXÉCUTER DU JS DANS LE NAVIGATEUR 📜 {details}",
63
+ "browser.automation.highlight_element": "SURLIGNER UN ÉLÉMENT DANS LE NAVIGATEUR 🔦 {details}",
64
+ "browser.automation.highlights_cleared": {
65
+ "one": "{count} surlignage effacé",
66
+ "other": "{count} surlignages effacés"
67
+ },
68
+ "browser.automation.js_executed": "JavaScript exécuté avec succès",
69
+ "browser.automation.js_failed": "Échec de l’exécution du JavaScript : {error}",
70
+ "browser.automation.list_workflows": "LISTER LES FLUX DE TRAVAIL",
71
+ "browser.automation.read_workflow": "LIRE LE FLUX DE TRAVAIL {details}",
72
+ "browser.automation.save_workflow": "ENREGISTRER LE FLUX DE TRAVAIL 💾 {details}",
73
+ "browser.automation.scroll": "FAIRE DÉFILER DANS LE NAVIGATEUR {details}",
74
+ "browser.automation.scroll_direction_unknown": "Direction de défilement inconnue : « {direction} »",
75
+ "browser.automation.scroll_to_element": "FAIRE DÉFILER JUSQU’À L’ÉLÉMENT DANS LE NAVIGATEUR 🎯 {details}",
76
+ "browser.automation.scrolled_element_down": "Défilement de l’élément « {selector} » vers le bas",
77
+ "browser.automation.scrolled_element_left": "Défilement de l’élément « {selector} » vers la gauche",
78
+ "browser.automation.scrolled_element_right": "Défilement de l’élément « {selector} » vers la droite",
79
+ "browser.automation.scrolled_element_up": "Défilement de l’élément « {selector} » vers le haut",
80
+ "browser.automation.scrolled_page_down": "Défilement de la page vers le bas",
81
+ "browser.automation.scrolled_page_left": "Défilement de la page vers la gauche",
82
+ "browser.automation.scrolled_page_right": "Défilement de la page vers la droite",
83
+ "browser.automation.scrolled_page_up": "Défilement de la page vers le haut",
84
+ "browser.automation.scrolled_to_element": "Défilement jusqu’à l’élément : {selector}",
85
+ "browser.automation.set_viewport": "DÉFINIR LA FENÊTRE D’AFFICHAGE DU NAVIGATEUR 🖥️ {details}",
86
+ "browser.automation.viewport_set": "Taille de la fenêtre d’affichage définie à {size}",
87
+ "browser.automation.wait_for_element": "ATTENDRE UN ÉLÉMENT DANS LE NAVIGATEUR {details}",
88
+ "browser.automation.workflow_not_found": "Flux de travail introuvable : {name}",
89
+ "browser.automation.workflow_read": {
90
+ "one": "Flux de travail lu avec succès : {count} caractère",
91
+ "other": "Flux de travail lu avec succès : {count} caractères"
92
+ },
93
+ "browser.automation.workflow_read_failed": "Échec de la lecture du flux de travail : {error}",
94
+ "browser.automation.workflow_save_failed": "Échec de l’enregistrement du flux de travail : {error}",
95
+ "browser.automation.workflow_saved": "Flux de travail enregistré avec succès : {path}",
96
+ "browser.automation.workflow_unreadable": "Impossible de lire {path} : {error}",
97
+ "browser.automation.workflows_found": {
98
+ "one": "{count} flux de travail trouvé",
99
+ "other": "{count} flux de travail trouvés"
100
+ },
101
+ "browser.automation.workflows_list_failed": "Impossible de lister les flux de travail : {error}",
56
102
  "browser.chromium.install_failed": "Chromium n’est pas installé et l’installation automatique a échoué (code de sortie {exit_code}). Exécutez cette commande et réessayez :\n{command}\n\nSortie de l’installateur :\n{output}",
57
103
  "browser.chromium.install_no_output": "L’installateur n’a produit aucune sortie.",
58
104
  "browser.chromium.install_start_failed": "Chromium n’est pas installé et son installateur n’a pas pu démarrer : {error}\nExécutez cette commande et réessayez :\n{command}",
@@ -436,6 +482,10 @@
436
482
  "mcp.install.no_description": "Aucune description disponible",
437
483
  "mcp.install.no_server_found": "Aucun serveur ne correspond à « {server} »",
438
484
  "mcp.install.registry_unavailable": "Registre de serveurs indisponible",
485
+ "mcp.install_menu.catalog_unavailable": "Catalogue de serveurs non disponible : {error}",
486
+ "mcp.install_menu.load_error": "Erreur de chargement du catalogue de serveurs : {error}",
487
+ "mcp.install_menu.no_catalog": "Aucun catalogue de serveurs MCP disponible.",
488
+ "mcp.install_menu.no_categories": "Aucune catégorie trouvée dans le catalogue de serveurs",
439
489
  "mcp.install_wizard.already_set": "Déjà configurée",
440
490
  "mcp.install_wizard.cancelled": "Installation annulée",
441
491
  "mcp.install_wizard.catalog_unavailable": "Catalogue de serveurs non disponible",
@@ -597,6 +647,8 @@
597
647
  "other": "✓ Également dissocié de {count} agents : {agents}"
598
648
  },
599
649
  "mcp.server.usage": "Utilisation : /mcp {command} <server_name>",
650
+ "mcp.startup.not_started": "Le serveur MCP « {server} » n’a pas démarré. Exécutez {logs_command} pour enquêter, ou dissociez-le via {agents_command}.",
651
+ "mcp.startup.stderr_line": "MCP {server} : {line}",
600
652
  "mcp.state.error": "⚠ Erreur",
601
653
  "mcp.state.quarantined": "⏸ Quarantaine",
602
654
  "mcp.state.running": "✓ Actif",
@@ -7,6 +7,9 @@ The neon splash starts BEFORE the cli_runner import below: that import pulls
7
7
  in pydantic-ai, prompt_toolkit, rich, and friends (~seconds of cold start),
8
8
  which is exactly the window the shimmer covers. ``code_puppy.splash`` is
9
9
  stdlib-only by design -- keep it that way, and keep it first.
10
+
11
+ In-tree plugins that must run setup before those heavy imports (see
12
+ ``code_puppy.early_startup``) are given their turn inside the same window.
10
13
  """
11
14
 
12
15
  from code_puppy.splash import start_splash
@@ -24,6 +27,11 @@ try:
24
27
  logging.getLogger(__name__).debug(
25
28
  "installing the error hooks failed", exc_info=True
26
29
  )
30
+ # Plugin setup that must precede the heavy imports. Failed hooks are
31
+ # recorded in errors.log before the runner import; never raises.
32
+ from code_puppy.early_startup import report_failures, run_early_startup
33
+
34
+ report_failures(run_early_startup())
27
35
  from code_puppy.cli_runner import main_entry
28
36
  finally:
29
37
  _splash.stop()
@@ -20,6 +20,7 @@ from typing import Any, List, Optional, Sequence
20
20
  from fastmcp.client.transports import StdioTransport
21
21
  from pydantic_ai.mcp import MCPToolset
22
22
 
23
+ from code_puppy.i18n import t
23
24
  from code_puppy.mcp_.mcp_logs import get_log_file_path, rotate_log_if_needed, write_log
24
25
  from code_puppy.messaging import emit_info
25
26
 
@@ -105,7 +106,11 @@ class StderrFileCapture:
105
106
  self.captured_lines.append(line)
106
107
  if self.emit_to_user:
107
108
  emit_info(
108
- f"MCP {self.server_name}: {line}",
109
+ t(
110
+ "mcp.startup.stderr_line",
111
+ server=self.server_name,
112
+ line=line,
113
+ ),
109
114
  message_group=self.message_group,
110
115
  )
111
116
  except Exception:
@@ -218,10 +223,14 @@ class BlockingStdioToolset(MCPToolset):
218
223
 
219
224
  # Point the user to /mcp logs; error_details stay out of the prompt
220
225
  # (already in the log file — no stack-trace spam on every run).
226
+ # Commands are placeholders so translations cannot break them.
221
227
  emit_info(
222
- f"MCP server '{self.server_name}' didn't start. "
223
- f"Run [cyan]/mcp logs {self.server_name}[/cyan] to investigate, "
224
- f"or unbind it via [cyan]/agents → B[/cyan].",
228
+ t(
229
+ "mcp.startup.not_started",
230
+ server=self.server_name,
231
+ logs_command=f"/mcp logs {self.server_name}",
232
+ agents_command="/agents → B",
233
+ ),
225
234
  style="yellow",
226
235
  message_group=self.message_group,
227
236
  )
@@ -281,97 +290,3 @@ class BlockingStdioToolset(MCPToolset):
281
290
  def is_ready(self) -> bool:
282
291
  """Check if server is ready without blocking."""
283
292
  return self._ready_event.is_set() and self._init_error is None
284
-
285
-
286
- class StartupMonitor:
287
- """
288
- Monitor for tracking multiple server startups.
289
-
290
- This class helps coordinate startup of multiple MCP servers
291
- and ensures all are ready before proceeding.
292
- """
293
-
294
- def __init__(self, message_group: Optional[uuid.UUID] = None):
295
- self.servers = {}
296
- self.startup_times = {}
297
- self.message_group = message_group or uuid.uuid4()
298
-
299
- def add_server(self, name: str, server: BlockingStdioToolset):
300
- """Add a server to monitor."""
301
- self.servers[name] = server
302
-
303
- async def wait_all_ready(self, timeout: float = 30.0) -> dict:
304
- """
305
- Wait for all servers to be ready.
306
-
307
- Args:
308
- timeout: Maximum time to wait for all servers
309
-
310
- Returns:
311
- Dictionary of server names to ready status
312
- """
313
- import time
314
-
315
- results = {}
316
-
317
- # Create tasks for all servers
318
- async def wait_server(name: str, server: BlockingStdioToolset):
319
- start = time.time()
320
- try:
321
- await server.wait_until_ready(timeout)
322
- self.startup_times[name] = time.time() - start
323
- results[name] = True
324
- emit_info(
325
- f" {name}: Ready in {self.startup_times[name]:.2f}s",
326
- style="dim green",
327
- message_group=self.message_group,
328
- )
329
- except Exception as e:
330
- self.startup_times[name] = time.time() - start
331
- results[name] = False
332
- emit_info(
333
- f" {name}: Failed after {self.startup_times[name]:.2f}s - {e}",
334
- style="dim red",
335
- message_group=self.message_group,
336
- )
337
-
338
- # Wait for all servers in parallel
339
- emit_info(
340
- f"Waiting for {len(self.servers)} MCP servers to initialize...",
341
- style="cyan",
342
- message_group=self.message_group,
343
- )
344
-
345
- tasks = [
346
- asyncio.create_task(wait_server(name, server))
347
- for name, server in self.servers.items()
348
- ]
349
-
350
- await asyncio.gather(*tasks, return_exceptions=True)
351
-
352
- # Report summary
353
- ready_count = sum(1 for r in results.values() if r)
354
- total_count = len(results)
355
-
356
- if ready_count == total_count:
357
- emit_info(
358
- f"All {total_count} servers ready!",
359
- style="green bold",
360
- message_group=self.message_group,
361
- )
362
- else:
363
- emit_info(
364
- f"{ready_count}/{total_count} servers ready",
365
- style="yellow",
366
- message_group=self.message_group,
367
- )
368
-
369
- return results
370
-
371
- def get_startup_report(self) -> str:
372
- """Get a report of startup times."""
373
- lines = ["Server Startup Times:"]
374
- for name, time_taken in self.startup_times.items():
375
- status = "[ok]" if self.servers[name].is_ready() else "[failed]"
376
- lines.append(f" {status} {name}: {time_taken:.2f}s")
377
- return "\n".join(lines)
@@ -14,7 +14,6 @@ _LEGACY_CLOSE_NOTICES = frozenset(
14
14
  "\u2713 Exited tutorial",
15
15
  "Exited custom server form",
16
16
  "Exited config settings menu",
17
- "Exited MCP server browser",
18
17
  "Exited UC tool browser",
19
18
  "Queue is empty",
20
19
  "\U0001f3a8 Theme unchanged.",