code-puppy 0.0.333__tar.gz → 0.0.335__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.333 → code_puppy-0.0.335}/PKG-INFO +1 -1
  2. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/cli_runner.py +40 -0
  3. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/add_model_menu.py +4 -0
  4. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/autosave_menu.py +5 -0
  5. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/colors_menu.py +5 -0
  6. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/config_commands.py +1 -1
  7. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/core_commands.py +51 -0
  8. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/diff_menu.py +5 -0
  9. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/custom_server_form.py +4 -0
  10. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/install_menu.py +5 -1
  11. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/model_settings_menu.py +5 -0
  12. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/motd.py +13 -7
  13. code_puppy-0.0.335/code_puppy/command_line/onboarding_slides.py +180 -0
  14. code_puppy-0.0.335/code_puppy/command_line/onboarding_wizard.py +340 -0
  15. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/config.py +3 -2
  16. code_puppy-0.0.335/code_puppy/http_utils.py +375 -0
  17. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/model_factory.py +86 -15
  18. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
  19. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
  20. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/antigravity_model.py +612 -0
  21. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/config.py +42 -0
  22. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/constants.py +136 -0
  23. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
  24. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
  25. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/storage.py +271 -0
  26. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
  27. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/token.py +167 -0
  28. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/transport.py +595 -0
  29. code_puppy-0.0.335/code_puppy/plugins/antigravity_oauth/utils.py +169 -0
  30. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +2 -0
  31. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +2 -0
  32. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/reopenable_async_client.py +8 -8
  33. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/command_runner.py +21 -6
  34. {code_puppy-0.0.333 → code_puppy-0.0.335}/pyproject.toml +1 -1
  35. code_puppy-0.0.333/code_puppy/http_utils.py +0 -416
  36. {code_puppy-0.0.333 → code_puppy-0.0.335}/.gitignore +0 -0
  37. {code_puppy-0.0.333 → code_puppy-0.0.335}/LICENSE +0 -0
  38. {code_puppy-0.0.333 → code_puppy-0.0.335}/README.md +0 -0
  39. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/__init__.py +0 -0
  40. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/__main__.py +0 -0
  41. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/__init__.py +0 -0
  42. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_c_reviewer.py +0 -0
  43. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_code_puppy.py +0 -0
  44. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_code_reviewer.py +0 -0
  45. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_cpp_reviewer.py +0 -0
  46. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_creator_agent.py +0 -0
  47. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_golang_reviewer.py +0 -0
  48. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_javascript_reviewer.py +0 -0
  49. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_manager.py +0 -0
  50. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_planning.py +0 -0
  51. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_python_programmer.py +0 -0
  52. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_python_reviewer.py +0 -0
  53. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_qa_expert.py +0 -0
  54. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_qa_kitten.py +0 -0
  55. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_security_auditor.py +0 -0
  56. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/agent_typescript_reviewer.py +0 -0
  57. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/base_agent.py +0 -0
  58. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/json_agent.py +0 -0
  59. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/agents/prompt_reviewer.py +0 -0
  60. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/callbacks.py +0 -0
  61. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/chatgpt_codex_client.py +0 -0
  62. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/claude_cache_client.py +0 -0
  63. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/__init__.py +0 -0
  64. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/attachments.py +0 -0
  65. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/command_handler.py +0 -0
  66. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/command_registry.py +0 -0
  67. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/file_path_completion.py +0 -0
  68. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/load_context_completion.py +0 -0
  69. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/__init__.py +0 -0
  70. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/add_command.py +0 -0
  71. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/base.py +0 -0
  72. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
  73. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
  74. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/edit_command.py +0 -0
  75. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/handler.py +0 -0
  76. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/help_command.py +0 -0
  77. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/install_command.py +0 -0
  78. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/list_command.py +0 -0
  79. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/logs_command.py +0 -0
  80. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/remove_command.py +0 -0
  81. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/restart_command.py +0 -0
  82. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/search_command.py +0 -0
  83. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/start_all_command.py +0 -0
  84. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/start_command.py +0 -0
  85. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/status_command.py +0 -0
  86. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
  87. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/stop_command.py +0 -0
  88. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/test_command.py +0 -0
  89. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/utils.py +0 -0
  90. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
  91. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/mcp_completion.py +0 -0
  92. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/model_picker_completion.py +0 -0
  93. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/pin_command_completion.py +0 -0
  94. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
  95. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/session_commands.py +0 -0
  96. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/command_line/utils.py +0 -0
  97. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/error_logging.py +0 -0
  98. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/gemini_code_assist.py +0 -0
  99. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/keymap.py +0 -0
  100. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/main.py +0 -0
  101. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/__init__.py +0 -0
  102. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/async_lifecycle.py +0 -0
  103. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/blocking_startup.py +0 -0
  104. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/captured_stdio_server.py +0 -0
  105. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/circuit_breaker.py +0 -0
  106. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/config_wizard.py +0 -0
  107. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/dashboard.py +0 -0
  108. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/error_isolation.py +0 -0
  109. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/examples/retry_example.py +0 -0
  110. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/health_monitor.py +0 -0
  111. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/managed_server.py +0 -0
  112. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/manager.py +0 -0
  113. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/mcp_logs.py +0 -0
  114. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/registry.py +0 -0
  115. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/retry_manager.py +0 -0
  116. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/server_registry_catalog.py +0 -0
  117. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/status_tracker.py +0 -0
  118. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/mcp_/system_tools.py +0 -0
  119. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/__init__.py +0 -0
  120. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/bus.py +0 -0
  121. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/commands.py +0 -0
  122. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/markdown_patches.py +0 -0
  123. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/message_queue.py +0 -0
  124. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/messages.py +0 -0
  125. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/queue_console.py +0 -0
  126. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/renderers.py +0 -0
  127. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/rich_renderer.py +0 -0
  128. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/spinner/__init__.py +0 -0
  129. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/spinner/console_spinner.py +0 -0
  130. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/messaging/spinner/spinner_base.py +0 -0
  131. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/model_utils.py +0 -0
  132. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/models.json +0 -0
  133. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/models_dev_api.json +0 -0
  134. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/models_dev_parser.py +0 -0
  135. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/__init__.py +0 -0
  136. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
  137. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
  138. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
  139. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
  140. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
  141. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
  142. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
  143. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
  144. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
  145. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
  146. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
  147. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
  148. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
  149. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/example_custom_command/README.md +0 -0
  150. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
  151. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
  152. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
  153. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/oauth_puppy_html.py +0 -0
  154. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/shell_safety/__init__.py +0 -0
  155. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
  156. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
  157. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
  158. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/prompts/codex_system_prompt.md +0 -0
  159. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/pydantic_patches.py +0 -0
  160. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/round_robin_model.py +0 -0
  161. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/session_storage.py +0 -0
  162. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/status_display.py +0 -0
  163. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/summarization_agent.py +0 -0
  164. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/terminal_utils.py +0 -0
  165. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/__init__.py +0 -0
  166. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/agent_tools.py +0 -0
  167. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/__init__.py +0 -0
  168. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/browser_control.py +0 -0
  169. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/browser_interactions.py +0 -0
  170. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/browser_locators.py +0 -0
  171. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/browser_navigation.py +0 -0
  172. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/browser_screenshot.py +0 -0
  173. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/browser_scripts.py +0 -0
  174. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/browser_workflows.py +0 -0
  175. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/camoufox_manager.py +0 -0
  176. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/browser/vqa_agent.py +0 -0
  177. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/common.py +0 -0
  178. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/file_modifications.py +0 -0
  179. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/file_operations.py +0 -0
  180. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/tools/tools_content.py +0 -0
  181. {code_puppy-0.0.333 → code_puppy-0.0.335}/code_puppy/uvx_detection.py +0 -0
  182. {code_puppy-0.0.333 → code_puppy-0.0.335}/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.333
3
+ Version: 0.0.335
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
@@ -364,6 +364,7 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
364
364
  emit_system_message(
365
365
  "Use /diff to configure diff highlighting colors for file changes."
366
366
  )
367
+ emit_system_message("To re-run the tutorial, use /tutorial.")
367
368
  try:
368
369
  from code_puppy.command_line.motd import print_motd
369
370
 
@@ -456,6 +457,45 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
456
457
 
457
458
  # Autosave loading is now manual - use /autosave_load command
458
459
 
460
+ # Auto-run tutorial on first startup
461
+ try:
462
+ from code_puppy.command_line.onboarding_wizard import should_show_onboarding
463
+
464
+ if should_show_onboarding():
465
+ import asyncio
466
+ import concurrent.futures
467
+
468
+ from code_puppy.command_line.onboarding_wizard import run_onboarding_wizard
469
+ from code_puppy.config import set_model_name
470
+ from code_puppy.messaging import emit_info
471
+
472
+ with concurrent.futures.ThreadPoolExecutor() as executor:
473
+ future = executor.submit(lambda: asyncio.run(run_onboarding_wizard()))
474
+ result = future.result(timeout=300)
475
+
476
+ if result == "chatgpt":
477
+ emit_info("🔐 Starting ChatGPT OAuth flow...")
478
+ from code_puppy.plugins.chatgpt_oauth.oauth_flow import run_oauth_flow
479
+
480
+ run_oauth_flow()
481
+ set_model_name("chatgpt-gpt-5.2-codex")
482
+ elif result == "claude":
483
+ emit_info("🔐 Starting Claude Code OAuth flow...")
484
+ from code_puppy.plugins.claude_code_oauth.register_callbacks import (
485
+ _perform_authentication,
486
+ )
487
+
488
+ _perform_authentication()
489
+ set_model_name("claude-code-claude-opus-4-5-20251101")
490
+ elif result == "completed":
491
+ emit_info("🎉 Tutorial complete! Happy coding!")
492
+ elif result == "skipped":
493
+ emit_info("⏭️ Tutorial skipped. Run /tutorial anytime!")
494
+ except Exception as e:
495
+ from code_puppy.messaging import emit_warning
496
+
497
+ emit_warning(f"Tutorial auto-start failed: {e}")
498
+
459
499
  # Track the current agent task for cancellation on quit
460
500
  current_agent_task = None
461
501
 
@@ -995,6 +995,10 @@ class AddModelMenu:
995
995
  # Reset awaiting input flag
996
996
  set_awaiting_user_input(False)
997
997
 
998
+ # Clear exit message (unless we're about to prompt for more input)
999
+ if self.result not in ("pending_credentials", "pending_custom_model"):
1000
+ emit_info("✓ Exited model browser")
1001
+
998
1002
  # Handle unsupported provider
999
1003
  if self.result == "unsupported" and self.current_provider:
1000
1004
  reason = UNSUPPORTED_PROVIDERS.get(
@@ -603,4 +603,9 @@ async def interactive_autosave_picker() -> Optional[str]:
603
603
  # Reset awaiting input flag
604
604
  set_awaiting_user_input(False)
605
605
 
606
+ # Clear exit message
607
+ from code_puppy.messaging import emit_info
608
+
609
+ emit_info("✓ Exited session browser")
610
+
606
611
  return result[0]
@@ -230,6 +230,11 @@ async def interactive_colors_picker() -> Optional[dict]:
230
230
  sys.stdout.write("\033[?1049l") # Exit alternate buffer
231
231
  sys.stdout.flush()
232
232
 
233
+ # Clear exit message
234
+ from code_puppy.messaging import emit_info
235
+
236
+ emit_info("✓ Exited banner color configuration")
237
+
233
238
  # Return changes if any
234
239
  if config.has_changes():
235
240
  return config.current_colors
@@ -46,7 +46,7 @@ def handle_show_command(command: str) -> bool:
46
46
  get_use_dbos,
47
47
  get_yolo_mode,
48
48
  )
49
- from code_puppy.keymap import get_cancel_agent_key, get_cancel_agent_display_name
49
+ from code_puppy.keymap import get_cancel_agent_display_name
50
50
  from code_puppy.messaging import emit_info
51
51
 
52
52
  puppy_name = get_puppy_name()
@@ -115,6 +115,57 @@ def handle_motd_command(command: str) -> bool:
115
115
  return True
116
116
 
117
117
 
118
+ @register_command(
119
+ name="tutorial",
120
+ description="Run the interactive tutorial wizard",
121
+ usage="/tutorial",
122
+ category="core",
123
+ )
124
+ def handle_tutorial_command(command: str) -> bool:
125
+ """Run the interactive tutorial wizard.
126
+
127
+ Usage:
128
+ /tutorial - Run the tutorial (can be run anytime)
129
+ """
130
+ import asyncio
131
+ import concurrent.futures
132
+
133
+ from code_puppy.command_line.onboarding_wizard import (
134
+ reset_onboarding,
135
+ run_onboarding_wizard,
136
+ )
137
+ from code_puppy.config import set_model_name
138
+
139
+ # Always reset so user can re-run the tutorial anytime
140
+ reset_onboarding()
141
+
142
+ # Run the async wizard in a thread pool (same pattern as agent picker)
143
+ with concurrent.futures.ThreadPoolExecutor() as executor:
144
+ future = executor.submit(lambda: asyncio.run(run_onboarding_wizard()))
145
+ result = future.result(timeout=300) # 5 min timeout
146
+
147
+ if result == "chatgpt":
148
+ emit_info("🔐 Starting ChatGPT OAuth flow...")
149
+ from code_puppy.plugins.chatgpt_oauth.oauth_flow import run_oauth_flow
150
+
151
+ run_oauth_flow()
152
+ set_model_name("chatgpt-gpt-5.2-codex")
153
+ elif result == "claude":
154
+ emit_info("🔐 Starting Claude Code OAuth flow...")
155
+ from code_puppy.plugins.claude_code_oauth.register_callbacks import (
156
+ _perform_authentication,
157
+ )
158
+
159
+ _perform_authentication()
160
+ set_model_name("claude-code-claude-opus-4-5-20251101")
161
+ elif result == "completed":
162
+ emit_info("🎉 Tutorial complete! Happy coding!")
163
+ elif result == "skipped":
164
+ emit_info("⏭️ Tutorial skipped. Run /tutorial anytime!")
165
+
166
+ return True
167
+
168
+
118
169
  @register_command(
119
170
  name="exit",
120
171
  description="Exit interactive mode",
@@ -456,6 +456,11 @@ async def interactive_diff_picker() -> Optional[dict]:
456
456
  sys.stdout.write("\033[?1049l") # Exit alternate buffer
457
457
  sys.stdout.flush()
458
458
 
459
+ # Clear exit message
460
+ from code_puppy.messaging import emit_info
461
+
462
+ emit_info("✓ Exited diff color configuration")
463
+
459
464
  # Return changes if any
460
465
  if config.has_changes():
461
466
  return {
@@ -604,6 +604,10 @@ class CustomServerForm:
604
604
  sys.stdout.flush()
605
605
  set_awaiting_user_input(False)
606
606
 
607
+ # Clear exit message if not installing
608
+ if self.result != "installed":
609
+ emit_info("✓ Exited custom server form")
610
+
607
611
  # Handle result
608
612
  if self.result == "installed":
609
613
  if self.edit_mode:
@@ -16,7 +16,7 @@ from prompt_toolkit.layout import Dimension, Layout, VSplit, Window
16
16
  from prompt_toolkit.layout.controls import FormattedTextControl
17
17
  from prompt_toolkit.widgets import Frame
18
18
 
19
- from code_puppy.messaging import emit_error, emit_warning
19
+ from code_puppy.messaging import emit_error, emit_info, emit_warning
20
20
  from code_puppy.tools.command_runner import set_awaiting_user_input
21
21
 
22
22
  from .catalog_server_installer import (
@@ -635,6 +635,10 @@ class MCPInstallMenu:
635
635
  sys.stdout.flush()
636
636
  set_awaiting_user_input(False)
637
637
 
638
+ # Clear exit message (unless we're about to prompt for more input)
639
+ if self.result not in ("pending_custom", "pending_install"):
640
+ emit_info("✓ Exited MCP server browser")
641
+
638
642
  # Handle custom server after TUI exits
639
643
  if self.result == "pending_custom":
640
644
  success = run_custom_server_form(self.manager)
@@ -835,6 +835,11 @@ class ModelSettingsMenu:
835
835
  sys.stdout.flush()
836
836
  set_awaiting_user_input(False)
837
837
 
838
+ # Clear exit message
839
+ from code_puppy.messaging import emit_info
840
+
841
+ emit_info("✓ Exited model settings")
842
+
838
843
  return self.result_changed
839
844
 
840
845
 
@@ -8,13 +8,19 @@ import os
8
8
  from code_puppy.config import CONFIG_DIR
9
9
  from code_puppy.messaging import emit_info
10
10
 
11
- MOTD_VERSION = "2025-11-27"
12
- MOTD_MESSAGE = """🐕‍🦺
13
- 🐾```
14
- # 🐶🦃🐕 November 27th, 2025 - Happy Thanksgiving! 🦃🐶
15
- 122k Downloads! 🎉
16
- Thank you for all the support!
17
- -Mike
11
+ MOTD_VERSION = "2026-01-01"
12
+ MOTD_MESSAGE = """
13
+ # 🐶 Happy New Year! January 1st, 2026 🎉
14
+ Reminder that Code Puppy supports three different OAuth subscriptions:
15
+
16
+ ### Claude Code - `/claude-code-auth`
17
+ - Opus / Haiku / Sonnet
18
+
19
+ ### ChatGPT Pro/Plus - `/chatgpt-auth`
20
+ - gpt-5.2 and gpt-5.2 codex
21
+
22
+ ### Google Antigravity - `/antigravity-auth`
23
+ - Gemini 3 Pro, Flash, and Anthropic models including Opus and Sonnet.
18
24
  """
19
25
  MOTD_TRACK_FILE = os.path.join(CONFIG_DIR, "motd.txt")
20
26
 
@@ -0,0 +1,180 @@
1
+ """Slide content for the onboarding wizard.
2
+
3
+ 🐶 Lean, mean, ADHD-friendly slides. 5 slides max!
4
+ """
5
+
6
+ from typing import List, Tuple
7
+
8
+ # ============================================================================
9
+ # Slide Data Constants
10
+ # ============================================================================
11
+
12
+ # Model subscription options
13
+ MODEL_OPTIONS: List[Tuple[str, str, str]] = [
14
+ ("chatgpt", "ChatGPT Plus/Pro/Max", "OAuth login - no API key needed"),
15
+ ("claude", "Claude Code Pro/Max", "OAuth login - no API key needed"),
16
+ ("api_keys", "API Keys", "OpenAI, Anthropic, Google, etc."),
17
+ ("openrouter", "OpenRouter", "Single key for 100+ models"),
18
+ ("skip", "Skip for now", "Configure later with /set or /add_model"),
19
+ ]
20
+
21
+
22
+ # ============================================================================
23
+ # Navigation Footer (shown on ALL slides)
24
+ # ============================================================================
25
+
26
+
27
+ def get_nav_footer() -> str:
28
+ """Navigation hints shown at bottom of every slide."""
29
+ return (
30
+ "\n[dim]─────────────────────────────────────[/dim]\n"
31
+ "[green]→/l[/green] Next "
32
+ "[green]←/h[/green] Back "
33
+ "[green]↑↓/jk[/green] Options "
34
+ "[green]Enter[/green] Select "
35
+ "[yellow]ESC[/yellow] Skip"
36
+ )
37
+
38
+
39
+ # ============================================================================
40
+ # Gradient Banner
41
+ # ============================================================================
42
+
43
+
44
+ def get_gradient_banner() -> str:
45
+ """Generate the gradient CODE PUPPY banner."""
46
+ try:
47
+ import pyfiglet
48
+
49
+ lines = pyfiglet.figlet_format("CODE PUPPY", font="ansi_shadow").split("\n")
50
+ colors = ["bright_blue", "bright_cyan", "bright_green"]
51
+ result = []
52
+ for i, line in enumerate(lines):
53
+ if line.strip():
54
+ color = colors[min(i // 2, len(colors) - 1)]
55
+ result.append(f"[{color}]{line}[/{color}]")
56
+ return "\n".join(result)
57
+ except ImportError:
58
+ return "[bold bright_cyan]═══ CODE PUPPY 🐶 ═══[/bold bright_cyan]"
59
+
60
+
61
+ # ============================================================================
62
+ # Slide Content (5 slides total)
63
+ # ============================================================================
64
+
65
+
66
+ def slide_welcome() -> str:
67
+ """Slide 1: Welcome - quick intro."""
68
+ content = get_gradient_banner()
69
+ content += "\n\n"
70
+ content += "[bold white]Welcome! 🐶[/bold white]\n\n"
71
+ content += "[cyan]Quick setup:[/cyan]\n"
72
+ content += " 1. Pick your model provider\n"
73
+ content += " 2. Optional: MCP servers\n"
74
+ content += " 3. Learn when to use which agent\n"
75
+ content += " 4. Start coding!\n\n"
76
+ content += "[dim]Takes ~1 minute. Let's go![/dim]"
77
+ content += get_nav_footer()
78
+ return content
79
+
80
+
81
+ def slide_models(selected_option: int, options: List[Tuple[str, str]]) -> str:
82
+ """Slide 2: Model selection."""
83
+ content = "[bold cyan]📦 Pick Your Models[/bold cyan]\n\n"
84
+ content += "[white]How do you want to access LLMs?[/white]\n\n"
85
+
86
+ for i, (_, label) in enumerate(options):
87
+ if i == selected_option:
88
+ content += f"[bold green]▶ {label}[/bold green]\n"
89
+ else:
90
+ content += f"[dim] {label}[/dim]\n"
91
+
92
+ content += "\n"
93
+
94
+ # Context based on selection
95
+ opt = options[selected_option][0] if options else None
96
+ if opt == "chatgpt":
97
+ content += "[yellow]💡 ChatGPT OAuth[/yellow]\n"
98
+ content += " Uses your existing subscription\n"
99
+ content += " GPT-5.2, GPT-5.2-codex\n"
100
+ elif opt == "claude":
101
+ content += "[yellow]💡 Claude OAuth[/yellow]\n"
102
+ content += " Uses your existing subscription\n"
103
+ content += " Opus/Sonnet/Haiku 4.5\n"
104
+ elif opt == "api_keys":
105
+ content += "[yellow]💡 API Keys[/yellow]\n"
106
+ content += " [cyan]/set OPENAI_API_KEY=sk-...[/cyan]\n"
107
+ content += " [cyan]/add_model[/cyan] to browse 1500+ models\n"
108
+ elif opt == "openrouter":
109
+ content += "[yellow]💡 OpenRouter[/yellow]\n"
110
+ content += " One API key, all providers\n"
111
+ content += " [cyan]/set OPENROUTER_API_KEY=...[/cyan]\n"
112
+ else:
113
+ content += "[dim]No worries! Use /set or /add_model later[/dim]\n"
114
+
115
+ content += get_nav_footer()
116
+ return content
117
+
118
+
119
+ def slide_mcp() -> str:
120
+ """Slide 3: MCP servers (optional power-ups)."""
121
+ content = "[bold cyan]🔌 MCP Servers (Optional)[/bold cyan]\n\n"
122
+ content += "[white]Supercharge with external tools![/white]\n\n"
123
+ content += "[green]Commands:[/green]\n"
124
+ content += " [cyan]/mcp install[/cyan] Browse catalog\n"
125
+ content += " [cyan]/mcp add[/cyan] Add custom server\n"
126
+ content += " [cyan]/mcp list[/cyan] See your servers\n\n"
127
+ content += "[yellow]🌟 Popular picks:[/yellow]\n"
128
+ content += " • GitHub integration\n"
129
+ content += " • Postgres/databases\n"
130
+ content += " • Slack, Linear, etc.\n\n"
131
+ content += "[dim]Skip this if you just want to code![/dim]"
132
+ content += get_nav_footer()
133
+ return content
134
+
135
+
136
+ def slide_use_cases() -> str:
137
+ """Slide 4: When to use which agent - THE IMPORTANT ONE."""
138
+ content = "[bold cyan]🎯 When to Use What[/bold cyan]\n\n"
139
+
140
+ content += "[bold yellow]🐶 Code Puppy (default)[/bold yellow]\n"
141
+ content += " [green]USE FOR:[/green] Direct coding tasks\n"
142
+ content += " • Fix this bug\n"
143
+ content += " • Add a feature to this file\n"
144
+ content += " • Refactor this function\n"
145
+ content += " • Write tests for X\n\n"
146
+
147
+ content += "[bold yellow]📋 Planning Agent[/bold yellow]\n"
148
+ content += " [green]USE FOR:[/green] Complex multi-step projects\n"
149
+ content += " • Build me a REST API with auth\n"
150
+ content += " • Create a CLI tool from scratch\n"
151
+ content += " • Refactor entire codebase\n"
152
+ content += " • Multi-file architectural changes\n\n"
153
+
154
+ content += "[cyan]Switch: /agent planning-agent[/cyan]\n"
155
+ content += "[dim]Planning breaks big tasks into steps,[/dim]\n"
156
+ content += "[dim]then delegates to specialists.[/dim]"
157
+ content += get_nav_footer()
158
+ return content
159
+
160
+
161
+ def slide_done(trigger_oauth: str | None) -> str:
162
+ """Slide 5: You're ready!"""
163
+ content = "[bold green]🎉 Ready to Roll![/bold green]\n\n"
164
+ content += "[bold cyan]Essential commands:[/bold cyan]\n"
165
+ content += " [cyan]/model[/cyan] Switch models\n"
166
+ content += " [cyan]/agent[/cyan] Switch agents\n"
167
+ content += " [cyan]/help[/cyan] All commands\n\n"
168
+
169
+ content += "[bold yellow]Pro tips:[/bold yellow]\n"
170
+ content += " • Be specific in prompts\n"
171
+ content += " • Use Planning Agent for big tasks\n"
172
+ content += " • @ for file path completion\n\n"
173
+
174
+ if trigger_oauth:
175
+ content += f"[bold cyan]→ {trigger_oauth.title()} OAuth next![/bold cyan]\n\n"
176
+
177
+ content += "[dim]Re-run anytime: [/dim][cyan]/tutorial[/cyan]\n"
178
+ content += "\n[bold yellow]Press Enter to start coding! 🐶[/bold yellow]"
179
+ content += get_nav_footer()
180
+ return content