code-puppy 0.0.338__tar.gz → 0.0.340__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 (182) hide show
  1. {code_puppy-0.0.338 → code_puppy-0.0.340}/PKG-INFO +2 -1
  2. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/claude_cache_client.py +76 -18
  3. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/cli_runner.py +46 -5
  4. code_puppy-0.0.340/code_puppy/command_line/clipboard.py +527 -0
  5. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/core_commands.py +34 -0
  6. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/prompt_toolkit_completion.py +112 -0
  7. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/managed_server.py +7 -11
  8. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/common.py +3 -9
  9. {code_puppy-0.0.338 → code_puppy-0.0.340}/pyproject.toml +2 -1
  10. {code_puppy-0.0.338 → code_puppy-0.0.340}/.gitignore +0 -0
  11. {code_puppy-0.0.338 → code_puppy-0.0.340}/LICENSE +0 -0
  12. {code_puppy-0.0.338 → code_puppy-0.0.340}/README.md +0 -0
  13. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/__init__.py +0 -0
  14. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/__main__.py +0 -0
  15. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/__init__.py +0 -0
  16. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_c_reviewer.py +0 -0
  17. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_code_puppy.py +0 -0
  18. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_code_reviewer.py +0 -0
  19. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_cpp_reviewer.py +0 -0
  20. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_creator_agent.py +0 -0
  21. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_golang_reviewer.py +0 -0
  22. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_javascript_reviewer.py +0 -0
  23. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_manager.py +0 -0
  24. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_planning.py +0 -0
  25. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_python_programmer.py +0 -0
  26. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_python_reviewer.py +0 -0
  27. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_qa_expert.py +0 -0
  28. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_qa_kitten.py +0 -0
  29. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_security_auditor.py +0 -0
  30. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/agent_typescript_reviewer.py +0 -0
  31. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/base_agent.py +0 -0
  32. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/json_agent.py +0 -0
  33. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/agents/prompt_reviewer.py +0 -0
  34. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/callbacks.py +0 -0
  35. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/chatgpt_codex_client.py +0 -0
  36. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/__init__.py +0 -0
  37. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/add_model_menu.py +0 -0
  38. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/attachments.py +0 -0
  39. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/autosave_menu.py +0 -0
  40. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/colors_menu.py +0 -0
  41. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/command_handler.py +0 -0
  42. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/command_registry.py +0 -0
  43. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/config_commands.py +0 -0
  44. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/diff_menu.py +0 -0
  45. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/file_path_completion.py +0 -0
  46. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/load_context_completion.py +0 -0
  47. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/__init__.py +0 -0
  48. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/add_command.py +0 -0
  49. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/base.py +0 -0
  50. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
  51. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
  52. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
  53. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/edit_command.py +0 -0
  54. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/handler.py +0 -0
  55. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/help_command.py +0 -0
  56. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/install_command.py +0 -0
  57. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/install_menu.py +0 -0
  58. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/list_command.py +0 -0
  59. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/logs_command.py +0 -0
  60. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/remove_command.py +0 -0
  61. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/restart_command.py +0 -0
  62. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/search_command.py +0 -0
  63. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/start_all_command.py +0 -0
  64. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/start_command.py +0 -0
  65. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/status_command.py +0 -0
  66. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
  67. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/stop_command.py +0 -0
  68. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/test_command.py +0 -0
  69. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/utils.py +0 -0
  70. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
  71. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/mcp_completion.py +0 -0
  72. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/model_picker_completion.py +0 -0
  73. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/model_settings_menu.py +0 -0
  74. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/motd.py +0 -0
  75. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/onboarding_slides.py +0 -0
  76. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/onboarding_wizard.py +0 -0
  77. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/pin_command_completion.py +0 -0
  78. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/session_commands.py +0 -0
  79. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/command_line/utils.py +0 -0
  80. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/config.py +0 -0
  81. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/error_logging.py +0 -0
  82. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/gemini_code_assist.py +0 -0
  83. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/http_utils.py +0 -0
  84. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/keymap.py +0 -0
  85. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/main.py +0 -0
  86. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/__init__.py +0 -0
  87. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/async_lifecycle.py +0 -0
  88. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/blocking_startup.py +0 -0
  89. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/captured_stdio_server.py +0 -0
  90. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/circuit_breaker.py +0 -0
  91. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/config_wizard.py +0 -0
  92. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/dashboard.py +0 -0
  93. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/error_isolation.py +0 -0
  94. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/examples/retry_example.py +0 -0
  95. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/health_monitor.py +0 -0
  96. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/manager.py +0 -0
  97. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/mcp_logs.py +0 -0
  98. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/registry.py +0 -0
  99. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/retry_manager.py +0 -0
  100. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/server_registry_catalog.py +0 -0
  101. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/status_tracker.py +0 -0
  102. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/mcp_/system_tools.py +0 -0
  103. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/__init__.py +0 -0
  104. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/bus.py +0 -0
  105. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/commands.py +0 -0
  106. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/markdown_patches.py +0 -0
  107. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/message_queue.py +0 -0
  108. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/messages.py +0 -0
  109. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/queue_console.py +0 -0
  110. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/renderers.py +0 -0
  111. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/rich_renderer.py +0 -0
  112. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/spinner/__init__.py +0 -0
  113. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/spinner/console_spinner.py +0 -0
  114. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/messaging/spinner/spinner_base.py +0 -0
  115. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/model_factory.py +0 -0
  116. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/model_utils.py +0 -0
  117. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/models.json +0 -0
  118. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/models_dev_api.json +0 -0
  119. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/models_dev_parser.py +0 -0
  120. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/__init__.py +0 -0
  121. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/__init__.py +0 -0
  122. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/accounts.py +0 -0
  123. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/antigravity_model.py +0 -0
  124. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/config.py +0 -0
  125. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/constants.py +0 -0
  126. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/oauth.py +0 -0
  127. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/register_callbacks.py +0 -0
  128. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/storage.py +0 -0
  129. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/test_plugin.py +0 -0
  130. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/token.py +0 -0
  131. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/transport.py +0 -0
  132. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/antigravity_oauth/utils.py +0 -0
  133. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
  134. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
  135. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
  136. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
  137. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
  138. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
  139. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
  140. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
  141. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
  142. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
  143. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
  144. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
  145. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
  146. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
  147. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
  148. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/example_custom_command/README.md +0 -0
  149. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
  150. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
  151. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
  152. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/oauth_puppy_html.py +0 -0
  153. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/shell_safety/__init__.py +0 -0
  154. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
  155. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
  156. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
  157. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/prompts/codex_system_prompt.md +0 -0
  158. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/pydantic_patches.py +0 -0
  159. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/reopenable_async_client.py +0 -0
  160. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/round_robin_model.py +0 -0
  161. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/session_storage.py +0 -0
  162. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/status_display.py +0 -0
  163. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/summarization_agent.py +0 -0
  164. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/terminal_utils.py +0 -0
  165. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/__init__.py +0 -0
  166. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/agent_tools.py +0 -0
  167. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/__init__.py +0 -0
  168. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/browser_control.py +0 -0
  169. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/browser_interactions.py +0 -0
  170. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/browser_locators.py +0 -0
  171. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/browser_navigation.py +0 -0
  172. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/browser_screenshot.py +0 -0
  173. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/browser_scripts.py +0 -0
  174. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/browser_workflows.py +0 -0
  175. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/camoufox_manager.py +0 -0
  176. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/browser/vqa_agent.py +0 -0
  177. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/command_runner.py +0 -0
  178. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/file_modifications.py +0 -0
  179. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/file_operations.py +0 -0
  180. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/tools/tools_content.py +0 -0
  181. {code_puppy-0.0.338 → code_puppy-0.0.340}/code_puppy/uvx_detection.py +0 -0
  182. {code_puppy-0.0.338 → code_puppy-0.0.340}/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.338
3
+ Version: 0.0.340
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
@@ -22,6 +22,7 @@ Requires-Dist: httpx[http2]>=0.24.1
22
22
  Requires-Dist: json-repair>=0.46.2
23
23
  Requires-Dist: logfire>=0.7.1
24
24
  Requires-Dist: openai>=1.99.1
25
+ Requires-Dist: pillow>=10.0.0
25
26
  Requires-Dist: playwright>=1.40.0
26
27
  Requires-Dist: prompt-toolkit>=3.0.52
27
28
  Requires-Dist: pydantic-ai==1.25.0
@@ -10,10 +10,13 @@ serialization, avoiding httpx/Pydantic internals.
10
10
  from __future__ import annotations
11
11
 
12
12
  import json
13
+ import logging
13
14
  from typing import Any, Callable, MutableMapping
14
15
 
15
16
  import httpx
16
17
 
18
+ logger = logging.getLogger(__name__)
19
+
17
20
  try:
18
21
  from anthropic import AsyncAnthropic
19
22
  except ImportError: # pragma: no cover - optional dep
@@ -58,25 +61,44 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
58
61
  pass
59
62
  response = await super().send(request, *args, **kwargs)
60
63
  try:
61
- if response.status_code == 401 and not request.extensions.get(
64
+ # Check for both 401 and 400 - Anthropic/Cloudflare may return 400 for auth errors
65
+ # Also check if it's a Cloudflare HTML error response
66
+ if response.status_code in (400, 401) and not request.extensions.get(
62
67
  "claude_oauth_refresh_attempted"
63
68
  ):
64
- refreshed_token = self._refresh_claude_oauth_token()
65
- if refreshed_token:
66
- await response.aclose()
67
- body_bytes = self._extract_body_bytes(request)
68
- headers = dict(request.headers)
69
- self._update_auth_headers(headers, refreshed_token)
70
- retry_request = self.build_request(
71
- method=request.method,
72
- url=request.url,
73
- headers=headers,
74
- content=body_bytes,
75
- )
76
- retry_request.extensions["claude_oauth_refresh_attempted"] = True
77
- return await super().send(retry_request, *args, **kwargs)
78
- except Exception:
79
- pass
69
+ # Determine if this is an auth error (including Cloudflare HTML errors)
70
+ is_auth_error = response.status_code == 401
71
+
72
+ if response.status_code == 400:
73
+ # Check if this is a Cloudflare HTML error
74
+ is_auth_error = self._is_cloudflare_html_error(response)
75
+ if is_auth_error:
76
+ logger.info(
77
+ "Detected Cloudflare 400 error (likely auth-related), attempting token refresh"
78
+ )
79
+
80
+ if is_auth_error:
81
+ refreshed_token = self._refresh_claude_oauth_token()
82
+ if refreshed_token:
83
+ logger.info("Token refreshed successfully, retrying request")
84
+ await response.aclose()
85
+ body_bytes = self._extract_body_bytes(request)
86
+ headers = dict(request.headers)
87
+ self._update_auth_headers(headers, refreshed_token)
88
+ retry_request = self.build_request(
89
+ method=request.method,
90
+ url=request.url,
91
+ headers=headers,
92
+ content=body_bytes,
93
+ )
94
+ retry_request.extensions["claude_oauth_refresh_attempted"] = (
95
+ True
96
+ )
97
+ return await super().send(retry_request, *args, **kwargs)
98
+ else:
99
+ logger.warning("Token refresh failed, returning original error")
100
+ except Exception as exc:
101
+ logger.debug("Error during token refresh attempt: %s", exc)
80
102
  return response
81
103
 
82
104
  @staticmethod
@@ -111,15 +133,51 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
111
133
  else:
112
134
  headers["Authorization"] = bearer_value
113
135
 
136
+ @staticmethod
137
+ def _is_cloudflare_html_error(response: httpx.Response) -> bool:
138
+ """Check if this is a Cloudflare HTML error response.
139
+
140
+ Cloudflare often returns HTML error pages with status 400 when
141
+ there are authentication issues.
142
+ """
143
+ # Check content type
144
+ content_type = response.headers.get("content-type", "")
145
+ if "text/html" not in content_type.lower():
146
+ return False
147
+
148
+ # Check if body contains Cloudflare markers
149
+ try:
150
+ # Read response body if not already consumed
151
+ if hasattr(response, "_content") and response._content:
152
+ body = response._content.decode("utf-8", errors="ignore")
153
+ else:
154
+ # Try to read the text (this might be already consumed)
155
+ try:
156
+ body = response.text
157
+ except Exception:
158
+ return False
159
+
160
+ # Look for Cloudflare and 400 Bad Request markers
161
+ body_lower = body.lower()
162
+ return "cloudflare" in body_lower and "400 bad request" in body_lower
163
+ except Exception as exc:
164
+ logger.debug("Error checking for Cloudflare error: %s", exc)
165
+ return False
166
+
114
167
  def _refresh_claude_oauth_token(self) -> str | None:
115
168
  try:
116
169
  from code_puppy.plugins.claude_code_oauth.utils import refresh_access_token
117
170
 
171
+ logger.info("Attempting to refresh Claude Code OAuth token...")
118
172
  refreshed_token = refresh_access_token(force=True)
119
173
  if refreshed_token:
120
174
  self._update_auth_headers(self.headers, refreshed_token)
175
+ logger.info("Successfully refreshed Claude Code OAuth token")
176
+ else:
177
+ logger.warning("Token refresh returned None")
121
178
  return refreshed_token
122
- except Exception:
179
+ except Exception as exc:
180
+ logger.error("Exception during token refresh: %s", exc)
123
181
  return None
124
182
 
125
183
  @staticmethod
@@ -22,6 +22,7 @@ from rich.console import Console
22
22
  from code_puppy import __version__, callbacks, plugins
23
23
  from code_puppy.agents import get_current_agent
24
24
  from code_puppy.command_line.attachments import parse_prompt_attachments
25
+ from code_puppy.command_line.clipboard import get_clipboard_manager
25
26
  from code_puppy.config import (
26
27
  AUTOSAVE_DIR,
27
28
  COMMAND_HISTORY_FILE,
@@ -143,8 +144,8 @@ async def main():
143
144
  except ImportError:
144
145
  emit_system_message("🐶 Code Puppy is Loading...")
145
146
 
146
- # Check for truecolor support and warn if not available
147
- print_truecolor_warning(display_console)
147
+ # Truecolor warning moved to interactive_mode() so it prints LAST
148
+ # after all the help stuff - max visibility for the ugly red box!
148
149
 
149
150
  available_port = find_available_port()
150
151
  if available_port is None:
@@ -354,6 +355,13 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
354
355
  emit_system_message(
355
356
  "Type @ for path completion, or /model to pick a model. Toggle multiline with Alt+M or F2; newline: Ctrl+J."
356
357
  )
358
+ emit_system_message("Paste images: Ctrl+V (even on Mac!), F3, or /paste command.")
359
+ import platform
360
+
361
+ if platform.system() == "Darwin":
362
+ emit_system_message(
363
+ "💡 macOS tip: Use Ctrl+V (not Cmd+V) to paste images in terminal."
364
+ )
357
365
  cancel_key = get_cancel_agent_display_name()
358
366
  emit_system_message(
359
367
  f"Press {cancel_key} during processing to cancel the current task or inference. Use Ctrl+X to interrupt running shell commands."
@@ -374,6 +382,10 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
374
382
 
375
383
  emit_warning(f"MOTD error: {e}")
376
384
 
385
+ # Print truecolor warning LAST so it's the most visible thing on startup
386
+ # Big ugly red box should be impossible to miss! 🔴
387
+ print_truecolor_warning(display_console)
388
+
377
389
  # Initialize the runtime agent manager
378
390
  if initial_command:
379
391
  from code_puppy.agents import get_current_agent
@@ -567,6 +579,7 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
567
579
 
568
580
  # Check for clear command (supports both `clear` and `/clear`)
569
581
  if task.strip().lower() in ("clear", "/clear"):
582
+ from code_puppy.command_line.clipboard import get_clipboard_manager
570
583
  from code_puppy.messaging import (
571
584
  emit_info,
572
585
  emit_system_message,
@@ -579,6 +592,13 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
579
592
  emit_warning("Conversation history cleared!")
580
593
  emit_system_message("The agent will not remember previous interactions.")
581
594
  emit_info(f"Auto-save session rotated to: {new_session_id}")
595
+
596
+ # Also clear pending clipboard images
597
+ clipboard_manager = get_clipboard_manager()
598
+ clipboard_count = clipboard_manager.get_pending_count()
599
+ clipboard_manager.clear_pending()
600
+ if clipboard_count > 0:
601
+ emit_info(f"Cleared {clipboard_count} pending clipboard image(s)")
582
602
  continue
583
603
 
584
604
  # Parse attachments first so leading paths aren't misread as commands
@@ -761,6 +781,7 @@ async def run_prompt_with_attachments(
761
781
  tuple: (result, task) where result is the agent response and task is the asyncio task
762
782
  """
763
783
  import asyncio
784
+ import re
764
785
 
765
786
  from code_puppy.messaging import emit_system_message, emit_warning
766
787
 
@@ -769,21 +790,41 @@ async def run_prompt_with_attachments(
769
790
  for warning in processed_prompt.warnings:
770
791
  emit_warning(warning)
771
792
 
793
+ # Get clipboard images and merge with file attachments
794
+ clipboard_manager = get_clipboard_manager()
795
+ clipboard_images = clipboard_manager.get_pending_images()
796
+
797
+ # Clear pending clipboard images after retrieval
798
+ clipboard_manager.clear_pending()
799
+
800
+ # Build summary of all attachments
772
801
  summary_parts = []
773
802
  if processed_prompt.attachments:
774
- summary_parts.append(f"binary files: {len(processed_prompt.attachments)}")
803
+ summary_parts.append(f"files: {len(processed_prompt.attachments)}")
804
+ if clipboard_images:
805
+ summary_parts.append(f"clipboard images: {len(clipboard_images)}")
775
806
  if processed_prompt.link_attachments:
776
807
  summary_parts.append(f"urls: {len(processed_prompt.link_attachments)}")
777
808
  if summary_parts:
778
809
  emit_system_message("Attachments detected -> " + ", ".join(summary_parts))
779
810
 
780
- if not processed_prompt.prompt:
811
+ # Clean up clipboard placeholders from the prompt text
812
+ cleaned_prompt = processed_prompt.prompt
813
+ if clipboard_images and cleaned_prompt:
814
+ cleaned_prompt = re.sub(
815
+ r"\[📋 clipboard image \d+\]\s*", "", cleaned_prompt
816
+ ).strip()
817
+
818
+ if not cleaned_prompt:
781
819
  emit_warning(
782
820
  "Prompt is empty after removing attachments; add instructions and retry."
783
821
  )
784
822
  return None, None
785
823
 
824
+ # Combine file attachments with clipboard images
786
825
  attachments = [attachment.content for attachment in processed_prompt.attachments]
826
+ attachments.extend(clipboard_images) # Add clipboard images
827
+
787
828
  link_attachments = [link.url_part for link in processed_prompt.link_attachments]
788
829
 
789
830
  # IMPORTANT: Set the shared console on the agent so that streaming output
@@ -795,7 +836,7 @@ async def run_prompt_with_attachments(
795
836
  # Create the agent task first so we can track and cancel it
796
837
  agent_task = asyncio.create_task(
797
838
  agent.run_with_mcp(
798
- processed_prompt.prompt,
839
+ cleaned_prompt, # Use cleaned prompt (clipboard placeholders removed)
799
840
  attachments=attachments,
800
841
  link_attachments=link_attachments,
801
842
  )