code-puppy 0.0.613__tar.gz → 0.0.615__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 (435) hide show
  1. {code_puppy-0.0.613 → code_puppy-0.0.615}/PKG-INFO +1 -1
  2. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/autosave_menu.py +8 -56
  3. code_puppy-0.0.615/code_puppy/plugins/grok_oauth/__init__.py +6 -0
  4. code_puppy-0.0.615/code_puppy/plugins/grok_oauth/config.py +49 -0
  5. code_puppy-0.0.615/code_puppy/plugins/grok_oauth/oauth_flow.py +213 -0
  6. code_puppy-0.0.615/code_puppy/plugins/grok_oauth/register_callbacks.py +132 -0
  7. code_puppy-0.0.615/code_puppy/plugins/grok_oauth/test_plugin.py +160 -0
  8. code_puppy-0.0.615/code_puppy/plugins/grok_oauth/utils.py +195 -0
  9. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/session_storage.py +9 -75
  10. {code_puppy-0.0.613 → code_puppy-0.0.615}/pyproject.toml +1 -1
  11. {code_puppy-0.0.613 → code_puppy-0.0.615}/.gitignore +0 -0
  12. {code_puppy-0.0.613 → code_puppy-0.0.615}/LICENSE +0 -0
  13. {code_puppy-0.0.613 → code_puppy-0.0.615}/README.md +0 -0
  14. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/__init__.py +0 -0
  15. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/__main__.py +0 -0
  16. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/__init__.py +0 -0
  17. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_builder.py +0 -0
  18. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_compaction.py +0 -0
  19. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_diagnostics.py +0 -0
  20. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_history.py +0 -0
  21. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_key_listeners.py +0 -0
  22. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_non_streaming_render.py +0 -0
  23. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_run_signals.py +0 -0
  24. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_runtime.py +0 -0
  25. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_steer_processor.py +0 -0
  26. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_code_puppy.py +0 -0
  27. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_creator_agent.py +0 -0
  28. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_helios.py +0 -0
  29. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_manager.py +0 -0
  30. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_planning.py +0 -0
  31. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_qa_kitten.py +0 -0
  32. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/base_agent.py +0 -0
  33. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/event_stream_handler.py +0 -0
  34. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/json_agent.py +0 -0
  35. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/run_stats.py +0 -0
  36. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/smooth_stream.py +0 -0
  37. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/subagent_stream_handler.py +0 -0
  38. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/callbacks.py +0 -0
  39. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/chatgpt_codex_client.py +0 -0
  40. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/claude_cache_client.py +0 -0
  41. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/cli_runner.py +0 -0
  42. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/__init__.py +0 -0
  43. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/add_model_menu.py +0 -0
  44. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/agent_menu.py +0 -0
  45. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/attachments.py +0 -0
  46. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/autosave_search.py +0 -0
  47. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/clipboard.py +0 -0
  48. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/colors_menu.py +0 -0
  49. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/command_handler.py +0 -0
  50. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/command_registry.py +0 -0
  51. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/config_apply.py +0 -0
  52. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/config_commands.py +0 -0
  53. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/core_commands.py +0 -0
  54. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/diff_menu.py +0 -0
  55. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/file_index.py +0 -0
  56. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/file_path_completion.py +0 -0
  57. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/judges_menu.py +0 -0
  58. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/load_context_completion.py +0 -0
  59. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/__init__.py +0 -0
  60. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/base.py +0 -0
  61. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
  62. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
  63. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
  64. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/edit_command.py +0 -0
  65. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/handler.py +0 -0
  66. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/help_command.py +0 -0
  67. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/install_command.py +0 -0
  68. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/install_menu.py +0 -0
  69. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/list_command.py +0 -0
  70. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/logs_command.py +0 -0
  71. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/remove_command.py +0 -0
  72. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/restart_command.py +0 -0
  73. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/search_command.py +0 -0
  74. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/silence_warning_command.py +0 -0
  75. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/start_all_command.py +0 -0
  76. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/start_command.py +0 -0
  77. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/status_command.py +0 -0
  78. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
  79. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/stop_command.py +0 -0
  80. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/utils.py +0 -0
  81. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
  82. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp_binding_menu.py +0 -0
  83. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp_completion.py +0 -0
  84. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/model_picker_completion.py +0 -0
  85. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/model_settings_menu.py +0 -0
  86. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/onboarding_slides.py +0 -0
  87. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/onboarding_wizard.py +0 -0
  88. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/pagination.py +0 -0
  89. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/pin_command_completion.py +0 -0
  90. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
  91. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/session_commands.py +0 -0
  92. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu.py +0 -0
  93. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_catalog.py +0 -0
  94. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_render.py +0 -0
  95. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_schema.py +0 -0
  96. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_settings.py +0 -0
  97. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_shims.py +0 -0
  98. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_values.py +0 -0
  99. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/shell_passthrough.py +0 -0
  100. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/skills_completion.py +0 -0
  101. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/uc_menu.py +0 -0
  102. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/utils.py +0 -0
  103. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/wiggum_state.py +0 -0
  104. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/config.py +0 -0
  105. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/error_logging.py +0 -0
  106. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/gemini_code_assist.py +0 -0
  107. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/gemini_model.py +0 -0
  108. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/README.md +0 -0
  109. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/__init__.py +0 -0
  110. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/aliases.py +0 -0
  111. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/engine.py +0 -0
  112. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/executor.py +0 -0
  113. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/matcher.py +0 -0
  114. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/models.py +0 -0
  115. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/registry.py +0 -0
  116. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/validator.py +0 -0
  117. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/http_utils.py +0 -0
  118. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/keymap.py +0 -0
  119. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/list_filtering.py +0 -0
  120. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/main.py +0 -0
  121. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/__init__.py +0 -0
  122. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/agent_bindings.py +0 -0
  123. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/async_lifecycle.py +0 -0
  124. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/blocking_startup.py +0 -0
  125. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/captured_stdio_server.py +0 -0
  126. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/circuit_breaker.py +0 -0
  127. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/config_wizard.py +0 -0
  128. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/dashboard.py +0 -0
  129. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/error_isolation.py +0 -0
  130. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/examples/retry_example.py +0 -0
  131. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/health_monitor.py +0 -0
  132. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/managed_server.py +0 -0
  133. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/manager.py +0 -0
  134. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/mcp_logs.py +0 -0
  135. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/registry.py +0 -0
  136. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/retry_manager.py +0 -0
  137. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/server_registry_catalog.py +0 -0
  138. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/status_tracker.py +0 -0
  139. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/system_tools.py +0 -0
  140. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/tool_arg_coercion.py +0 -0
  141. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_prompts/__init__.py +0 -0
  142. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_prompts/hook_creator.py +0 -0
  143. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/__init__.py +0 -0
  144. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/bar_painters.py +0 -0
  145. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/bar_rendering.py +0 -0
  146. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/bottom_bar.py +0 -0
  147. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/bus.py +0 -0
  148. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/commands.py +0 -0
  149. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_actions.py +0 -0
  150. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_completion.py +0 -0
  151. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_display.py +0 -0
  152. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_history.py +0 -0
  153. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_keys.py +0 -0
  154. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_paste.py +0 -0
  155. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/line_editor.py +0 -0
  156. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/markdown_patches.py +0 -0
  157. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/message_queue.py +0 -0
  158. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/messages.py +0 -0
  159. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/pause_controller.py +0 -0
  160. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/prompt_prefix_style.py +0 -0
  161. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/queue_console.py +0 -0
  162. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/renderers.py +0 -0
  163. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/rich_renderer.py +0 -0
  164. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/run_ui.py +0 -0
  165. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/run_ui_wiring.py +0 -0
  166. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/spinner/__init__.py +0 -0
  167. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/subagent_console.py +0 -0
  168. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/transcript_guard.py +0 -0
  169. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/model_descriptions.py +0 -0
  170. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/model_factory.py +0 -0
  171. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/model_switching.py +0 -0
  172. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/model_utils.py +0 -0
  173. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/models.json +0 -0
  174. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/models_dev_api.json +0 -0
  175. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/models_dev_parser.py +0 -0
  176. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/__init__.py +0 -0
  177. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/__init__.py +0 -0
  178. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/config.py +0 -0
  179. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/discovery.py +0 -0
  180. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/downloader.py +0 -0
  181. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/enabled_skills.py +0 -0
  182. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/installer.py +0 -0
  183. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/metadata.py +0 -0
  184. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/prompt_builder.py +0 -0
  185. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/register_callbacks.py +0 -0
  186. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/remote_catalog.py +0 -0
  187. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/skill_catalog.py +0 -0
  188. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/skill_commands.py +0 -0
  189. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/skills_install_menu.py +0 -0
  190. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/skills_menu.py +0 -0
  191. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/aws_bedrock/__init__.py +0 -0
  192. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/aws_bedrock/config.py +0 -0
  193. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/aws_bedrock/register_callbacks.py +0 -0
  194. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/aws_bedrock/utils.py +0 -0
  195. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/README.md +0 -0
  196. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/__init__.py +0 -0
  197. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/config.py +0 -0
  198. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/discovery.py +0 -0
  199. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/register_callbacks.py +0 -0
  200. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/token.py +0 -0
  201. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/utils.py +0 -0
  202. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/btw/__init__.py +0 -0
  203. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/btw/inline_view.py +0 -0
  204. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/btw/register_callbacks.py +0 -0
  205. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/btw/side_query.py +0 -0
  206. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
  207. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
  208. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
  209. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
  210. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
  211. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
  212. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_hooks/__init__.py +0 -0
  213. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_hooks/config.py +0 -0
  214. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_hooks/register_callbacks.py +0 -0
  215. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
  216. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
  217. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
  218. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
  219. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/fast_mode.py +0 -0
  220. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/prompt_handler.py +0 -0
  221. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
  222. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/test_fast_mode.py +0 -0
  223. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
  224. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +0 -0
  225. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
  226. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/code_puppy_agent/SKILL.md +0 -0
  227. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/code_puppy_agent/__init__.py +0 -0
  228. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/code_puppy_agent/register_callbacks.py +0 -0
  229. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/config.py +0 -0
  230. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/context_indicator/__init__.py +0 -0
  231. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/context_indicator/register_callbacks.py +0 -0
  232. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/context_indicator/usage.py +0 -0
  233. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/__init__.py +0 -0
  234. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/config.py +0 -0
  235. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/reasoning_client.py +0 -0
  236. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/register_callbacks.py +0 -0
  237. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/utils.py +0 -0
  238. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
  239. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
  240. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/__init__.py +0 -0
  241. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/cancel.py +0 -0
  242. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/commands.py +0 -0
  243. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/config.py +0 -0
  244. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/lifecycle.py +0 -0
  245. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/register_callbacks.py +0 -0
  246. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/runtime.py +0 -0
  247. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/startup_lock.py +0 -0
  248. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/workflow_ids.py +0 -0
  249. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/wrapper.py +0 -0
  250. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/destructive_command_guard/__init__.py +0 -0
  251. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/destructive_command_guard/detector.py +0 -0
  252. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/destructive_command_guard/register_callbacks.py +0 -0
  253. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/emoji_filter/__init__.py +0 -0
  254. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/emoji_filter/config.py +0 -0
  255. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/emoji_filter/register_callbacks.py +0 -0
  256. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/emoji_filter/stripper.py +0 -0
  257. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/example_custom_command/README.md +0 -0
  258. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
  259. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
  260. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
  261. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/force_push_guard/__init__.py +0 -0
  262. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/force_push_guard/detector.py +0 -0
  263. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/force_push_guard/register_callbacks.py +0 -0
  264. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/force_push_guard/test_detector.py +0 -0
  265. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/fork/__init__.py +0 -0
  266. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/fork/register_callbacks.py +0 -0
  267. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/frontend_emitter/__init__.py +0 -0
  268. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/frontend_emitter/emitter.py +0 -0
  269. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/frontend_emitter/register_callbacks.py +0 -0
  270. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/frontend_emitter/session_context.py +0 -0
  271. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/README.md +0 -0
  272. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/__init__.py +0 -0
  273. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/client.py +0 -0
  274. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/register_callbacks.py +0 -0
  275. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/reporter.py +0 -0
  276. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_creator/__init__.py +0 -0
  277. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_creator/register_callbacks.py +0 -0
  278. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_manager/__init__.py +0 -0
  279. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_manager/config.py +0 -0
  280. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_manager/hooks_menu.py +0 -0
  281. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_manager/register_callbacks.py +0 -0
  282. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/no_tools/__init__.py +0 -0
  283. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/no_tools/register_callbacks.py +0 -0
  284. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/oauth_puppy_html.py +0 -0
  285. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/obsidian_agent/README.md +0 -0
  286. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/obsidian_agent/__init__.py +0 -0
  287. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/obsidian_agent/agent_obsidian.py +0 -0
  288. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/obsidian_agent/register_callbacks.py +0 -0
  289. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama/__init__.py +0 -0
  290. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama/register_callbacks.py +0 -0
  291. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama_setup/__init__.py +0 -0
  292. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama_setup/completer.py +0 -0
  293. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama_setup/register_callbacks.py +0 -0
  294. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/__init__.py +0 -0
  295. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugin_contributions.py +0 -0
  296. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugin_meta.py +0 -0
  297. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugin_text_utils.py +0 -0
  298. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugins_menu.py +0 -0
  299. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugins_menu_layout.py +0 -0
  300. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugins_menu_render.py +0 -0
  301. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/project_trust_flow.py +0 -0
  302. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/register_callbacks.py +0 -0
  303. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/pop_command/__init__.py +0 -0
  304. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/pop_command/register_callbacks.py +0 -0
  305. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prompt_newline/__init__.py +0 -0
  306. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prompt_newline/config.py +0 -0
  307. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prompt_newline/register_callbacks.py +0 -0
  308. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/__init__.py +0 -0
  309. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/prune_menu.py +0 -0
  310. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/prune_model.py +0 -0
  311. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/prune_render.py +0 -0
  312. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/register_callbacks.py +0 -0
  313. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/README.md +0 -0
  314. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/__init__.py +0 -0
  315. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/commands.py +0 -0
  316. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/config.py +0 -0
  317. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/kennel.py +0 -0
  318. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/packer.py +0 -0
  319. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/recorder.py +0 -0
  320. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/register_callbacks.py +0 -0
  321. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/retriever.py +0 -0
  322. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/schema.py +0 -0
  323. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/state.py +0 -0
  324. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/tools.py +0 -0
  325. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/wings.py +0 -0
  326. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/README.md +0 -0
  327. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/__init__.py +0 -0
  328. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/builtin_frames.py +0 -0
  329. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/commands.py +0 -0
  330. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/picker.py +0 -0
  331. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/register_callbacks.py +0 -0
  332. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/spinners.py +0 -0
  333. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/quick_resume/__init__.py +0 -0
  334. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/quick_resume/register_callbacks.py +0 -0
  335. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/review_pr/__init__.py +0 -0
  336. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/review_pr/register_callbacks.py +0 -0
  337. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/shell_safety/__init__.py +0 -0
  338. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
  339. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
  340. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
  341. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/stack_dump/__init__.py +0 -0
  342. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/stack_dump/register_callbacks.py +0 -0
  343. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/__init__.py +0 -0
  344. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/config.py +0 -0
  345. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/payload.py +0 -0
  346. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/prompt_patch.py +0 -0
  347. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/register_callbacks.py +0 -0
  348. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/runner.py +0 -0
  349. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/statusline_command.py +0 -0
  350. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/steer_queue/__init__.py +0 -0
  351. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/steer_queue/queue_menu.py +0 -0
  352. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/steer_queue/register_callbacks.py +0 -0
  353. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/README.md +0 -0
  354. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/__init__.py +0 -0
  355. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/coalesce_patch.py +0 -0
  356. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/panel_render.py +0 -0
  357. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/register_callbacks.py +0 -0
  358. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/resume_repaint.py +0 -0
  359. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/state.py +0 -0
  360. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/switch_agent_resume/__init__.py +0 -0
  361. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/switch_agent_resume/register_callbacks.py +0 -0
  362. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/README.md +0 -0
  363. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/__init__.py +0 -0
  364. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/bundled_palettes.py +0 -0
  365. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/content_styles.py +0 -0
  366. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/osc_palette.py +0 -0
  367. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/picker.py +0 -0
  368. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/register_callbacks.py +0 -0
  369. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/rich_themes.py +0 -0
  370. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/themes.py +0 -0
  371. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/token_ratio_learner/__init__.py +0 -0
  372. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/token_ratio_learner/ratios.py +0 -0
  373. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/token_ratio_learner/register_callbacks.py +0 -0
  374. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/trust.py +0 -0
  375. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/trust_notice.py +0 -0
  376. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/__init__.py +0 -0
  377. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/models.py +0 -0
  378. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/register_callbacks.py +0 -0
  379. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/registry.py +0 -0
  380. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/sandbox.py +0 -0
  381. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wide_completion_menu/__init__.py +0 -0
  382. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wide_completion_menu/register_callbacks.py +0 -0
  383. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/__init__.py +0 -0
  384. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/judge.py +0 -0
  385. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/judge_config.py +0 -0
  386. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/register_callbacks.py +0 -0
  387. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/state.py +0 -0
  388. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/provider_credentials.py +0 -0
  389. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/provider_identity.py +0 -0
  390. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/pydantic_patches.py +0 -0
  391. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/reopenable_async_client.py +0 -0
  392. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/round_robin_model.py +0 -0
  393. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/session_lifecycle.py +0 -0
  394. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/session_migration.py +0 -0
  395. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/status_display.py +0 -0
  396. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/summarization_agent.py +0 -0
  397. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/terminal_utils.py +0 -0
  398. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/__init__.py +0 -0
  399. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/agent_tools.py +0 -0
  400. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/__init__.py +0 -0
  401. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/constants.py +0 -0
  402. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
  403. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/handler.py +0 -0
  404. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/models.py +0 -0
  405. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/registration.py +0 -0
  406. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/renderers.py +0 -0
  407. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
  408. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/theme.py +0 -0
  409. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
  410. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/__init__.py +0 -0
  411. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_control.py +0 -0
  412. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_interactions.py +0 -0
  413. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_locator_resolver.py +0 -0
  414. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_locators.py +0 -0
  415. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_manager.py +0 -0
  416. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_navigation.py +0 -0
  417. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_page_snapshot.py +0 -0
  418. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_screenshot.py +0 -0
  419. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_scripts.py +0 -0
  420. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_semantic_interactions.py +0 -0
  421. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_workflows.py +0 -0
  422. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/command_runner.py +0 -0
  423. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/common.py +0 -0
  424. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/display.py +0 -0
  425. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/file_modifications.py +0 -0
  426. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/file_operations.py +0 -0
  427. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/image_tools.py +0 -0
  428. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/model_tools.py +0 -0
  429. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/skills_tools.py +0 -0
  430. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/subagent_context.py +0 -0
  431. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/subagent_invocation.py +0 -0
  432. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/tools_content.py +0 -0
  433. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/universal_constructor.py +0 -0
  434. {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/undo_manager.py +0 -0
  435. {code_puppy-0.0.613 → code_puppy-0.0.615}/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.613
3
+ Version: 0.0.615
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
@@ -48,26 +48,8 @@ def _get_session_metadata(base_dir: Path, session_name: str) -> dict:
48
48
  return {}
49
49
 
50
50
 
51
- def _is_auto_flavored(session_name: str) -> bool:
52
- """Return True for system-generated session names.
53
-
54
- Matches the ``auto_session_`` prefix reserved by
55
- ``code_puppy.session_lifecycle.is_valid_session_name``. Pre-unification,
56
- all autosaves carried this prefix; the unified-autosave migration introduces user-named
57
- entries (from ``-r NAME``, ``/dump_context``, ``/load_context``) into
58
- the same store, hence the need to distinguish.
59
- """
60
- return session_name.startswith("auto_session_")
61
-
62
-
63
51
  def _get_session_entries(base_dir: Path) -> List[Tuple[str, dict]]:
64
- """Get all sessions with their metadata.
65
-
66
- Returns entries grouped "named first, auto-flavored second", each group
67
- sorted mtime-descending. Section headers (if any) are inserted at
68
- render time -- see ``_render_menu_panel`` -- to keep this function's
69
- return shape stable and pagination math straightforward.
70
- """
52
+ """Get all sessions with their metadata, most recent first."""
71
53
  try:
72
54
  sessions = list_sessions(base_dir)
73
55
  except (FileNotFoundError, PermissionError):
@@ -93,11 +75,8 @@ def _get_session_entries(base_dir: Path) -> List[Tuple[str, dict]]:
93
75
  return datetime.min
94
76
  return datetime.min
95
77
 
96
- named = [e for e in entries if not _is_auto_flavored(e[0])]
97
- auto = [e for e in entries if _is_auto_flavored(e[0])]
98
- named.sort(key=sort_key, reverse=True)
99
- auto.sort(key=sort_key, reverse=True)
100
- return named + auto
78
+ entries.sort(key=sort_key, reverse=True)
79
+ return entries
101
80
 
102
81
 
103
82
  def _extract_last_user_message(history: list) -> str:
@@ -237,24 +216,9 @@ def _render_menu_panel(
237
216
  lines.append(("", "Cancel"))
238
217
  return lines
239
218
 
240
- # Show sessions for current page. We inject visual section headers
241
- # at named->auto boundaries within the visible page (and at the very
242
- # top if the page starts in either section). Section headers do NOT
243
- # consume entry indices -- they're cosmetic only -- so selection and
244
- # pagination math stay untouched.
245
- prev_section = None
246
219
  for i in range(start_idx, end_idx):
247
220
  session_name, metadata = entries[i]
248
221
  is_selected = i == selected_idx
249
- section = "auto" if _is_auto_flavored(session_name) else "named"
250
-
251
- if section != prev_section:
252
- header = "Named sessions" if section == "named" else "Auto-saved sessions"
253
- # Blank line between sections (skip for the very first one).
254
- if prev_section is not None:
255
- lines.append(("", "\n"))
256
- lines.append(("fg:ansibrightcyan bold", f" {header}\n"))
257
- prev_section = section
258
222
 
259
223
  # Format timestamp
260
224
  timestamp = metadata.get("timestamp", "unknown")
@@ -264,20 +228,13 @@ def _render_menu_panel(
264
228
  except Exception:
265
229
  time_str = "unknown time"
266
230
 
267
- # Format message count
231
+ # Format message count. auto_session_* names are opaque noise — hide them.
232
+ # User-named sessions keep the parenthetical so they stay distinguishable.
268
233
  msg_count = metadata.get("message_count", "?")
269
-
270
- # For named sessions, include the name so users can tell them
271
- # apart at a glance. Auto-flavored sessions get the deterministic
272
- # label persisted at save time (#246) -- '[project] first prompt' --
273
- # since the auto_session_* names are noise.
274
- if section == "named":
275
- label = f"{time_str} \u2022 {msg_count} msgs ({session_name})"
276
- else:
234
+ if session_name.startswith("auto_session_"):
277
235
  label = f"{time_str} \u2022 {msg_count} msgs"
278
- derived = metadata.get("label")
279
- if derived:
280
- label = f"{label} \u2014 {derived}"
236
+ else:
237
+ label = f"{time_str} \u2022 {msg_count} msgs ({session_name})"
281
238
 
282
239
  # Highlight selected item
283
240
  if is_selected:
@@ -427,11 +384,6 @@ def _render_preview_panel(base_dir: Path, entry: Optional[Tuple[str, dict]]) ->
427
384
  lines.append(("", session_name))
428
385
  lines.append(("", "\n"))
429
386
 
430
- derived = metadata.get("label")
431
- if derived:
432
- lines.append(("fg:ansibrightblack", f" Label: {derived}"))
433
- lines.append(("", "\n"))
434
-
435
387
  timestamp = metadata.get("timestamp", "unknown")
436
388
  try:
437
389
  dt = datetime.fromisoformat(timestamp)
@@ -0,0 +1,6 @@
1
+ """Grok (x.ai) OAuth plugin for Code Puppy.
2
+
3
+ Authenticates against xAI's OAuth service (the same flow used by the
4
+ official Grok CLI and the pi-xai-oauth package) and registers Grok
5
+ models that run on the resulting access token.
6
+ """
@@ -0,0 +1,49 @@
1
+ """Configuration for the Grok (x.ai) OAuth plugin.
2
+
3
+ Endpoints, client id, and scopes mirror the official Grok CLI OAuth flow
4
+ (as implemented by https://pi.dev/packages/pi-xai-oauth).
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Any, Dict
9
+
10
+ from code_puppy import config
11
+
12
+ GROK_OAUTH_CONFIG: Dict[str, Any] = {
13
+ # OIDC issuer + discovery. Authorization/token endpoints are resolved
14
+ # dynamically from the discovery document and validated against *.x.ai.
15
+ "issuer": "https://auth.x.ai",
16
+ "discovery_url": "https://auth.x.ai/.well-known/openid-configuration",
17
+ # Public OAuth client used by the official Grok CLI.
18
+ "client_id": "b1a00492-073a-47ea-816f-4c329264a828",
19
+ "scope": "openid profile email offline_access grok-cli:access api:access",
20
+ # Localhost callback. xAI allows an ephemeral-port fallback when the
21
+ # preferred port is busy.
22
+ "redirect_host": "127.0.0.1",
23
+ "redirect_port": 56121,
24
+ "redirect_path": "/callback",
25
+ "callback_timeout": 180,
26
+ # Refresh this many seconds before the token actually expires.
27
+ "refresh_skew_seconds": 120,
28
+ # Grok models are served from the regular xAI API with the OAuth
29
+ # access token as the bearer key.
30
+ "api_base_url": "https://api.x.ai/v1",
31
+ # Registered model names are prefixed to avoid catalogue collisions.
32
+ "prefix": "xai-",
33
+ "default_model": "grok-4.5",
34
+ }
35
+
36
+ # Grok models made available after authentication.
37
+ GROK_MODELS: Dict[str, Dict[str, Any]] = {
38
+ "grok-4.5": {
39
+ "context_length": 500_000,
40
+ "supported_settings": ["reasoning_effort"],
41
+ },
42
+ }
43
+
44
+
45
+ def get_token_storage_path() -> Path:
46
+ """Path for storing OAuth tokens (uses XDG_DATA_HOME)."""
47
+ data_dir = Path(config.DATA_DIR)
48
+ data_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
49
+ return data_dir / "grok_oauth.json"
@@ -0,0 +1,213 @@
1
+ """Grok (x.ai) OAuth flow matching the official Grok CLI / pi-xai-oauth."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import secrets
6
+ import threading
7
+ import time
8
+ import urllib.parse
9
+ from http.server import BaseHTTPRequestHandler, HTTPServer
10
+ from typing import Any
11
+
12
+ from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
13
+
14
+ from ..oauth_puppy_html import oauth_failure_html, oauth_success_html
15
+ from .config import GROK_OAUTH_CONFIG
16
+ from .utils import (
17
+ exchange_token,
18
+ fetch_discovery,
19
+ generate_pkce_pair,
20
+ load_stored_tokens,
21
+ save_tokens,
22
+ tokens_from_payload,
23
+ )
24
+
25
+ _ALLOWED_CORS_ORIGINS = ("https://accounts.x.ai", "https://auth.x.ai")
26
+
27
+
28
+ class _OAuthServer(HTTPServer):
29
+ """Localhost callback server holding the state for one OAuth attempt."""
30
+
31
+ def __init__(self) -> None:
32
+ host = GROK_OAUTH_CONFIG["redirect_host"]
33
+ preferred_port = GROK_OAUTH_CONFIG["redirect_port"]
34
+ try:
35
+ super().__init__((host, preferred_port), _CallbackHandler)
36
+ except OSError:
37
+ # Preferred port busy — xAI accepts ephemeral localhost ports.
38
+ super().__init__((host, 0), _CallbackHandler)
39
+
40
+ self.exit_code = 1
41
+ discovery = fetch_discovery()
42
+ self.authorization_endpoint = discovery["authorization_endpoint"]
43
+ self.token_endpoint = discovery["token_endpoint"]
44
+ self.state = secrets.token_hex(16)
45
+ self.nonce = secrets.token_hex(16)
46
+ self.code_verifier, self.code_challenge = generate_pkce_pair()
47
+ port = self.server_address[1]
48
+ self.redirect_uri = f"http://{host}:{port}{GROK_OAUTH_CONFIG['redirect_path']}"
49
+
50
+ def auth_url(self) -> str:
51
+ params = {
52
+ "response_type": "code",
53
+ "client_id": GROK_OAUTH_CONFIG["client_id"],
54
+ "redirect_uri": self.redirect_uri,
55
+ "scope": GROK_OAUTH_CONFIG["scope"],
56
+ "code_challenge": self.code_challenge,
57
+ "code_challenge_method": "S256",
58
+ "state": self.state,
59
+ "nonce": self.nonce,
60
+ }
61
+ return f"{self.authorization_endpoint}?{urllib.parse.urlencode(params)}"
62
+
63
+ def exchange_code(self, code: str) -> bool:
64
+ payload = exchange_token(
65
+ self.token_endpoint,
66
+ {
67
+ "grant_type": "authorization_code",
68
+ "code": code,
69
+ "redirect_uri": self.redirect_uri,
70
+ "client_id": GROK_OAUTH_CONFIG["client_id"],
71
+ "code_verifier": self.code_verifier,
72
+ },
73
+ )
74
+ return save_tokens(tokens_from_payload(payload, self.token_endpoint))
75
+
76
+
77
+ class _CallbackHandler(BaseHTTPRequestHandler):
78
+ server: "_OAuthServer"
79
+
80
+ def do_OPTIONS(self) -> None: # noqa: N802
81
+ self.send_response(204)
82
+ self._send_cors_headers()
83
+ self.end_headers()
84
+
85
+ def do_GET(self) -> None: # noqa: N802
86
+ parsed = urllib.parse.urlparse(self.path)
87
+ if parsed.path != GROK_OAUTH_CONFIG["redirect_path"]:
88
+ self._send_failure(404, "Callback endpoint not found for the puppy parade.")
89
+ return
90
+
91
+ params = urllib.parse.parse_qs(parsed.query)
92
+ error = params.get("error", [None])[0]
93
+ if error:
94
+ description = params.get("error_description", [error])[0]
95
+ self._send_failure(400, f"xAI authorization failed: {description}")
96
+ self._shutdown_after_delay()
97
+ return
98
+
99
+ if params.get("state", [None])[0] != self.server.state:
100
+ self._send_failure(
101
+ 400, "OAuth state mismatch — a squirrel may have tampered with it."
102
+ )
103
+ return
104
+
105
+ code = params.get("code", [None])[0]
106
+ if not code:
107
+ self._send_failure(400, "Missing auth code — the token treat rolled away.")
108
+ self._shutdown_after_delay()
109
+ return
110
+
111
+ try:
112
+ saved = self.server.exchange_code(code)
113
+ except Exception as exc: # noqa: BLE001
114
+ self._send_failure(500, f"Token exchange failed: {exc}")
115
+ self._shutdown_after_delay()
116
+ return
117
+
118
+ if saved:
119
+ self.server.exit_code = 0
120
+ self._send_html(
121
+ oauth_success_html(
122
+ "Grok",
123
+ "You can now close this window and return to Code Puppy.",
124
+ )
125
+ )
126
+ else:
127
+ self._send_failure(
128
+ 500, "Unable to persist auth file — a puppy probably chewed it."
129
+ )
130
+ self._shutdown_after_delay()
131
+
132
+ def log_message(self, fmt: str, *args: Any) -> None: # noqa: A003
133
+ pass
134
+
135
+ def _send_cors_headers(self) -> None:
136
+ origin = self.headers.get("Origin")
137
+ if origin not in _ALLOWED_CORS_ORIGINS:
138
+ return
139
+ self.send_header("Access-Control-Allow-Origin", origin)
140
+ self.send_header("Access-Control-Allow-Methods", "GET, OPTIONS")
141
+ self.send_header("Access-Control-Allow-Headers", "Content-Type")
142
+ self.send_header("Access-Control-Allow-Private-Network", "true")
143
+ self.send_header("Vary", "Origin")
144
+
145
+ def _send_html(self, body: str, status: int = 200) -> None:
146
+ encoded = body.encode("utf-8")
147
+ self.send_response(status)
148
+ self._send_cors_headers()
149
+ self.send_header("Content-Type", "text/html; charset=utf-8")
150
+ self.send_header("Content-Length", str(len(encoded)))
151
+ self.end_headers()
152
+ self.wfile.write(encoded)
153
+
154
+ def _send_failure(self, status: int, reason: str) -> None:
155
+ self._send_html(oauth_failure_html("Grok", reason), status)
156
+
157
+ def _shutdown_after_delay(self, seconds: float = 2.0) -> None:
158
+ def _later() -> None:
159
+ time.sleep(seconds)
160
+ self.server.shutdown()
161
+
162
+ threading.Thread(target=_later, daemon=True).start()
163
+
164
+
165
+ def run_oauth_flow() -> bool:
166
+ """Run the browser OAuth flow. Returns True when tokens were saved."""
167
+ existing_tokens = load_stored_tokens()
168
+ if existing_tokens and existing_tokens.get("access_token"):
169
+ emit_warning("Existing Grok tokens will be overwritten.")
170
+
171
+ try:
172
+ server = _OAuthServer()
173
+ except Exception as exc: # noqa: BLE001
174
+ emit_error(f"Could not start Grok OAuth flow: {exc}")
175
+ return False
176
+
177
+ auth_url = server.auth_url()
178
+ emit_info(f"Open this URL in your browser: {auth_url}")
179
+
180
+ server_thread = threading.Thread(target=server.serve_forever, daemon=True)
181
+ server_thread.start()
182
+
183
+ from code_puppy.tools.common import should_suppress_browser
184
+
185
+ if should_suppress_browser():
186
+ emit_info(f"[HEADLESS MODE] Would normally open: {auth_url}")
187
+ else:
188
+ try:
189
+ import webbrowser
190
+
191
+ if not webbrowser.open(auth_url):
192
+ emit_warning("Please open the URL manually — the browser stayed home.")
193
+ except Exception as exc: # noqa: BLE001
194
+ emit_warning(f"Could not open browser automatically: {exc}")
195
+
196
+ emit_info("Waiting for xAI authentication callback…")
197
+
198
+ elapsed = 0.0
199
+ timeout = GROK_OAUTH_CONFIG["callback_timeout"]
200
+ interval = 0.25
201
+ while elapsed < timeout and server.exit_code != 0:
202
+ time.sleep(interval)
203
+ elapsed += interval
204
+
205
+ server.shutdown()
206
+ server_thread.join(timeout=5)
207
+
208
+ if server.exit_code != 0:
209
+ emit_error("Grok authentication failed or timed out.")
210
+ return False
211
+
212
+ emit_success("Grok OAuth complete — access token saved.")
213
+ return True
@@ -0,0 +1,132 @@
1
+ """Grok (x.ai) OAuth plugin callbacks.
2
+
3
+ Provides OAuth authentication for Grok models (SuperGrok / Grok CLI flow)
4
+ and registers the 'grok_oauth' model type handler plus the Grok model
5
+ catalogue (headlined by grok-4.5).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict, List, Optional, Tuple
11
+
12
+ from code_puppy.callbacks import register_callback
13
+ from code_puppy.messaging import emit_info, emit_success, emit_warning
14
+ from code_puppy.model_switching import set_model_and_reload_agent
15
+
16
+ from .config import GROK_MODELS, GROK_OAUTH_CONFIG, get_token_storage_path
17
+ from .oauth_flow import run_oauth_flow
18
+ from .utils import get_valid_access_token, load_stored_tokens
19
+
20
+ _PREFIX = GROK_OAUTH_CONFIG["prefix"]
21
+ _DEFAULT_MODEL = f"{_PREFIX}{GROK_OAUTH_CONFIG['default_model']}"
22
+
23
+
24
+ def _custom_help() -> List[Tuple[str, str]]:
25
+ return [
26
+ (
27
+ "grok-auth",
28
+ "Authenticate with x.ai (Grok) via OAuth and register Grok models",
29
+ ),
30
+ ("grok-status", "Check Grok OAuth authentication status"),
31
+ ("grok-logout", "Remove Grok OAuth tokens and registered models"),
32
+ ]
33
+
34
+
35
+ def _handle_grok_status() -> None:
36
+ tokens = load_stored_tokens()
37
+ if tokens and tokens.get("access_token"):
38
+ emit_success(" Grok OAuth: Authenticated")
39
+ emit_info(f" Available Grok models: {', '.join(_grok_model_names())}")
40
+ else:
41
+ emit_warning(" Grok OAuth: Not authenticated")
42
+ emit_info(" Run /grok-auth to launch the browser sign-in flow.")
43
+
44
+
45
+ def _handle_grok_logout() -> None:
46
+ token_path = get_token_storage_path()
47
+ if token_path.exists():
48
+ token_path.unlink()
49
+ emit_info("Removed Grok OAuth tokens")
50
+ emit_success("Grok logout complete")
51
+
52
+
53
+ def _handle_custom_command(command: str, name: str) -> Optional[bool]:
54
+ if not name:
55
+ return None
56
+
57
+ if name == "grok-auth":
58
+ if run_oauth_flow():
59
+ emit_info(f"Registering Grok models: {', '.join(_grok_model_names())}")
60
+ set_model_and_reload_agent(_DEFAULT_MODEL)
61
+ return True
62
+
63
+ if name == "grok-status":
64
+ _handle_grok_status()
65
+ return True
66
+
67
+ if name == "grok-logout":
68
+ _handle_grok_logout()
69
+ return True
70
+
71
+ return None
72
+
73
+
74
+ def _grok_model_names() -> List[str]:
75
+ return [f"{_PREFIX}{model_id}" for model_id in GROK_MODELS]
76
+
77
+
78
+ def _load_grok_models() -> Dict[str, Any]:
79
+ """Inject Grok models into the catalogue once authenticated."""
80
+ tokens = load_stored_tokens()
81
+ if not (tokens and tokens.get("access_token")):
82
+ return {}
83
+
84
+ return {
85
+ f"{_PREFIX}{model_id}": {
86
+ "type": "grok_oauth",
87
+ "name": model_id,
88
+ "context_length": spec["context_length"],
89
+ "supported_settings": spec.get("supported_settings", []),
90
+ "oauth_source": "grok-oauth-plugin",
91
+ }
92
+ for model_id, spec in GROK_MODELS.items()
93
+ }
94
+
95
+
96
+ def _create_grok_oauth_model(model_name: str, model_config: Dict, config: Dict) -> Any:
97
+ """Create a Grok OAuth model instance.
98
+
99
+ This handler is registered via the 'register_model_type' callback to
100
+ handle models with type='grok_oauth'. Grok models speak the OpenAI
101
+ Responses API on api.x.ai with the OAuth access token as bearer key.
102
+ """
103
+ from pydantic_ai.models.openai import OpenAIResponsesModel
104
+ from pydantic_ai.providers.openai import OpenAIProvider
105
+
106
+ from code_puppy.http_utils import create_async_client
107
+
108
+ access_token = get_valid_access_token()
109
+ if not access_token:
110
+ emit_warning(
111
+ f"Failed to get valid Grok OAuth token; skipping model "
112
+ f"'{model_config.get('name')}'. Run /grok-auth to authenticate."
113
+ )
114
+ return None
115
+
116
+ provider = OpenAIProvider(
117
+ api_key=access_token,
118
+ base_url=GROK_OAUTH_CONFIG["api_base_url"],
119
+ http_client=create_async_client(model_name=model_name),
120
+ )
121
+ return OpenAIResponsesModel(model_name=model_config["name"], provider=provider)
122
+
123
+
124
+ def _register_model_types() -> List[Dict[str, Any]]:
125
+ """Register the grok_oauth model type handler."""
126
+ return [{"type": "grok_oauth", "handler": _create_grok_oauth_model}]
127
+
128
+
129
+ register_callback("custom_command_help", _custom_help)
130
+ register_callback("custom_command", _handle_custom_command)
131
+ register_callback("register_model_type", _register_model_types)
132
+ register_callback("load_models_config", _load_grok_models)
@@ -0,0 +1,160 @@
1
+ """Tests for the Grok (x.ai) OAuth plugin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from unittest.mock import patch
7
+
8
+ import pytest
9
+
10
+ from code_puppy.plugins.grok_oauth import config as grok_config
11
+ from code_puppy.plugins.grok_oauth import register_callbacks as rc
12
+ from code_puppy.plugins.grok_oauth import utils
13
+ from code_puppy.plugins.grok_oauth.config import GROK_OAUTH_CONFIG
14
+
15
+
16
+ @pytest.fixture
17
+ def token_path(tmp_path):
18
+ path = tmp_path / "grok_oauth.json"
19
+ with (
20
+ patch.object(grok_config, "get_token_storage_path", return_value=path),
21
+ patch.object(utils, "get_token_storage_path", return_value=path),
22
+ patch.object(rc, "get_token_storage_path", return_value=path),
23
+ ):
24
+ yield path
25
+
26
+
27
+ def _save_valid_tokens(**overrides):
28
+ tokens = {
29
+ "access_token": "fake-token",
30
+ "refresh_token": "fake-refresh",
31
+ "expires_at": time.time() + 3600,
32
+ "token_endpoint": "https://auth.x.ai/oauth2/token",
33
+ }
34
+ tokens.update(overrides)
35
+ assert utils.save_tokens(tokens)
36
+ return tokens
37
+
38
+
39
+ def test_config_values():
40
+ assert GROK_OAUTH_CONFIG["issuer"] == "https://auth.x.ai"
41
+ assert "grok-cli:access" in GROK_OAUTH_CONFIG["scope"]
42
+ assert "api:access" in GROK_OAUTH_CONFIG["scope"]
43
+ assert GROK_OAUTH_CONFIG["api_base_url"] == "https://api.x.ai/v1"
44
+ assert GROK_OAUTH_CONFIG["default_model"] in grok_config.GROK_MODELS
45
+
46
+
47
+ def test_pkce_pair_shape():
48
+ verifier, challenge = utils.generate_pkce_pair()
49
+ assert len(verifier) >= 43
50
+ assert "=" not in verifier and "=" not in challenge
51
+ # Deterministic: same verifier always hashes to the same challenge
52
+ import base64
53
+ import hashlib
54
+
55
+ digest = hashlib.sha256(verifier.encode()).digest()
56
+ expected = base64.urlsafe_b64encode(digest).decode().rstrip("=")
57
+ assert challenge == expected
58
+
59
+
60
+ @pytest.mark.parametrize(
61
+ "url",
62
+ [
63
+ "http://auth.x.ai/oauth2/token", # not https
64
+ "https://evil.com/oauth2/token", # wrong host
65
+ "https://notx.ai/oauth2/token", # suffix trickery
66
+ "https://x.ai.evil.com/token", # prefix trickery
67
+ ],
68
+ )
69
+ def test_validate_xai_endpoint_rejects(url):
70
+ with pytest.raises(ValueError):
71
+ utils.validate_xai_endpoint(url)
72
+
73
+
74
+ def test_validate_xai_endpoint_accepts():
75
+ assert utils.validate_xai_endpoint("https://auth.x.ai/oauth2/token")
76
+ assert utils.validate_xai_endpoint("https://x.ai/oauth2/token")
77
+
78
+
79
+ def test_save_and_load_tokens(token_path):
80
+ _save_valid_tokens()
81
+ loaded = utils.load_stored_tokens()
82
+ assert loaded["access_token"] == "fake-token"
83
+ assert oct(token_path.stat().st_mode & 0o777) == oct(0o600)
84
+
85
+
86
+ def test_get_valid_access_token_fresh(token_path):
87
+ _save_valid_tokens()
88
+ assert utils.get_valid_access_token() == "fake-token"
89
+
90
+
91
+ def test_get_valid_access_token_refreshes_expired(token_path):
92
+ _save_valid_tokens(access_token="stale", expires_at=time.time() - 10)
93
+ payload = {
94
+ "access_token": "fresh-token",
95
+ "refresh_token": "new-refresh",
96
+ "expires_in": 3600,
97
+ }
98
+ with patch.object(utils, "exchange_token", return_value=payload) as exchange:
99
+ assert utils.get_valid_access_token() == "fresh-token"
100
+
101
+ body = exchange.call_args[0][1]
102
+ assert body["grant_type"] == "refresh_token"
103
+ assert body["client_id"] == GROK_OAUTH_CONFIG["client_id"]
104
+ assert utils.load_stored_tokens()["refresh_token"] == "new-refresh"
105
+
106
+
107
+ def test_get_valid_access_token_unauthenticated(token_path):
108
+ assert utils.get_valid_access_token() is None
109
+
110
+
111
+ def test_load_grok_models_requires_auth(token_path):
112
+ assert rc._load_grok_models() == {}
113
+
114
+
115
+ def test_load_grok_models_when_authenticated(token_path):
116
+ _save_valid_tokens()
117
+ models = rc._load_grok_models()
118
+ assert "xai-grok-4.5" in models
119
+ entry = models["xai-grok-4.5"]
120
+ assert entry["type"] == "grok_oauth"
121
+ assert entry["name"] == "grok-4.5"
122
+ assert entry["context_length"] == 500_000
123
+
124
+
125
+ def test_model_type_registration():
126
+ handlers = rc._register_model_types()
127
+ assert handlers == [{"type": "grok_oauth", "handler": rc._create_grok_oauth_model}]
128
+
129
+
130
+ def test_create_model_returns_none_without_auth(token_path):
131
+ model = rc._create_grok_oauth_model("xai-grok-4.5", {"name": "grok-4.5"}, {})
132
+ assert model is None
133
+
134
+
135
+ def test_create_model_when_authenticated(token_path):
136
+ _save_valid_tokens()
137
+ model = rc._create_grok_oauth_model("xai-grok-4.5", {"name": "grok-4.5"}, {})
138
+ assert type(model).__name__ == "OpenAIResponsesModel"
139
+ assert model.model_name == "grok-4.5"
140
+
141
+
142
+ def test_custom_command_ignores_unknown():
143
+ assert rc._handle_custom_command("/other", "not-ours") is None
144
+ assert rc._handle_custom_command("/other", "") is None
145
+
146
+
147
+ def test_custom_command_handles_status(token_path):
148
+ assert rc._handle_custom_command("/grok-status", "grok-status") is True
149
+
150
+
151
+ def test_custom_command_logout(token_path):
152
+ _save_valid_tokens()
153
+ assert rc._handle_custom_command("/grok-logout", "grok-logout") is True
154
+ assert not token_path.exists()
155
+
156
+
157
+ def test_custom_help_entries():
158
+ entries = rc._custom_help()
159
+ names = [name for name, _ in entries]
160
+ assert names == ["grok-auth", "grok-status", "grok-logout"]