code-puppy-core-plugins 0.0.2__py3-none-any.whl

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 (313) hide show
  1. code_puppy_core_plugins/__init__.py +5 -0
  2. code_puppy_core_plugins/acp/README.md +266 -0
  3. code_puppy_core_plugins/acp/__init__.py +8 -0
  4. code_puppy_core_plugins/acp/agent.py +494 -0
  5. code_puppy_core_plugins/acp/bridge.py +309 -0
  6. code_puppy_core_plugins/acp/capabilities.py +73 -0
  7. code_puppy_core_plugins/acp/commands.py +116 -0
  8. code_puppy_core_plugins/acp/content.py +115 -0
  9. code_puppy_core_plugins/acp/io_delegation.py +231 -0
  10. code_puppy_core_plugins/acp/mcp_config.py +66 -0
  11. code_puppy_core_plugins/acp/permissions.py +159 -0
  12. code_puppy_core_plugins/acp/persistence.py +197 -0
  13. code_puppy_core_plugins/acp/register_callbacks.py +110 -0
  14. code_puppy_core_plugins/acp/replay.py +114 -0
  15. code_puppy_core_plugins/acp/session.py +329 -0
  16. code_puppy_core_plugins/acp/session_config.py +179 -0
  17. code_puppy_core_plugins/acp/state.py +135 -0
  18. code_puppy_core_plugins/agent_skills/__init__.py +22 -0
  19. code_puppy_core_plugins/agent_skills/config.py +208 -0
  20. code_puppy_core_plugins/agent_skills/discovery.py +337 -0
  21. code_puppy_core_plugins/agent_skills/downloader.py +392 -0
  22. code_puppy_core_plugins/agent_skills/enabled_skills.py +82 -0
  23. code_puppy_core_plugins/agent_skills/installer.py +22 -0
  24. code_puppy_core_plugins/agent_skills/metadata.py +219 -0
  25. code_puppy_core_plugins/agent_skills/prompt_builder.py +49 -0
  26. code_puppy_core_plugins/agent_skills/provider.py +52 -0
  27. code_puppy_core_plugins/agent_skills/register_callbacks.py +309 -0
  28. code_puppy_core_plugins/agent_skills/remote_catalog.py +322 -0
  29. code_puppy_core_plugins/agent_skills/skill_catalog.py +257 -0
  30. code_puppy_core_plugins/agent_skills/skill_commands.py +102 -0
  31. code_puppy_core_plugins/agent_skills/skills_install_menu.py +698 -0
  32. code_puppy_core_plugins/agent_skills/skills_menu.py +794 -0
  33. code_puppy_core_plugins/aws_bedrock/__init__.py +14 -0
  34. code_puppy_core_plugins/aws_bedrock/config.py +101 -0
  35. code_puppy_core_plugins/aws_bedrock/register_callbacks.py +243 -0
  36. code_puppy_core_plugins/aws_bedrock/utils.py +186 -0
  37. code_puppy_core_plugins/azure_foundry/README.md +238 -0
  38. code_puppy_core_plugins/azure_foundry/__init__.py +15 -0
  39. code_puppy_core_plugins/azure_foundry/config.py +126 -0
  40. code_puppy_core_plugins/azure_foundry/discovery.py +189 -0
  41. code_puppy_core_plugins/azure_foundry/register_callbacks.py +506 -0
  42. code_puppy_core_plugins/azure_foundry/token.py +182 -0
  43. code_puppy_core_plugins/azure_foundry/utils.py +375 -0
  44. code_puppy_core_plugins/btw/__init__.py +1 -0
  45. code_puppy_core_plugins/btw/inline_view.py +135 -0
  46. code_puppy_core_plugins/btw/register_callbacks.py +106 -0
  47. code_puppy_core_plugins/btw/side_query.py +91 -0
  48. code_puppy_core_plugins/chatgpt_oauth/IMAGEGEN_SKILL.md +83 -0
  49. code_puppy_core_plugins/chatgpt_oauth/__init__.py +8 -0
  50. code_puppy_core_plugins/chatgpt_oauth/config.py +52 -0
  51. code_puppy_core_plugins/chatgpt_oauth/image_generation.py +253 -0
  52. code_puppy_core_plugins/chatgpt_oauth/image_tool.py +70 -0
  53. code_puppy_core_plugins/chatgpt_oauth/oauth_flow.py +461 -0
  54. code_puppy_core_plugins/chatgpt_oauth/register_callbacks.py +288 -0
  55. code_puppy_core_plugins/chatgpt_oauth/test_plugin.py +663 -0
  56. code_puppy_core_plugins/chatgpt_oauth/usage.py +113 -0
  57. code_puppy_core_plugins/chatgpt_oauth/utils.py +534 -0
  58. code_puppy_core_plugins/claude_code_hooks/__init__.py +1 -0
  59. code_puppy_core_plugins/claude_code_hooks/config.py +137 -0
  60. code_puppy_core_plugins/claude_code_hooks/register_callbacks.py +383 -0
  61. code_puppy_core_plugins/claude_code_oauth/README.md +167 -0
  62. code_puppy_core_plugins/claude_code_oauth/SETUP.md +93 -0
  63. code_puppy_core_plugins/claude_code_oauth/__init__.py +25 -0
  64. code_puppy_core_plugins/claude_code_oauth/config.py +59 -0
  65. code_puppy_core_plugins/claude_code_oauth/fast_mode.py +122 -0
  66. code_puppy_core_plugins/claude_code_oauth/prompt_handler.py +64 -0
  67. code_puppy_core_plugins/claude_code_oauth/register_callbacks.py +719 -0
  68. code_puppy_core_plugins/claude_code_oauth/test_plugin.py +285 -0
  69. code_puppy_core_plugins/claude_code_oauth/token_refresh_heartbeat.py +240 -0
  70. code_puppy_core_plugins/claude_code_oauth/utils.py +663 -0
  71. code_puppy_core_plugins/code_puppy_agent/AGENTS_AND_TOOLS.md +155 -0
  72. code_puppy_core_plugins/code_puppy_agent/MODELS_AND_MCP.md +104 -0
  73. code_puppy_core_plugins/code_puppy_agent/PLUGINS_AND_CALLBACKS.md +147 -0
  74. code_puppy_core_plugins/code_puppy_agent/SESSIONS_AND_HISTORY.md +39 -0
  75. code_puppy_core_plugins/code_puppy_agent/SKILL.md +180 -0
  76. code_puppy_core_plugins/code_puppy_agent/SKILLS_SYSTEM.md +136 -0
  77. code_puppy_core_plugins/code_puppy_agent/SYSTEM_PROMPT_CONFIG_AND_I18N.md +160 -0
  78. code_puppy_core_plugins/code_puppy_agent/__init__.py +5 -0
  79. code_puppy_core_plugins/code_puppy_agent/register_callbacks.py +46 -0
  80. code_puppy_core_plugins/computer_use/NativeCapture.swift +123 -0
  81. code_puppy_core_plugins/computer_use/README.md +109 -0
  82. code_puppy_core_plugins/computer_use/__init__.py +1 -0
  83. code_puppy_core_plugins/computer_use/accessibility.py +163 -0
  84. code_puppy_core_plugins/computer_use/activation.py +75 -0
  85. code_puppy_core_plugins/computer_use/backend.py +584 -0
  86. code_puppy_core_plugins/computer_use/backend_types.py +5 -0
  87. code_puppy_core_plugins/computer_use/capture.py +110 -0
  88. code_puppy_core_plugins/computer_use/commands.py +62 -0
  89. code_puppy_core_plugins/computer_use/geometry.py +81 -0
  90. code_puppy_core_plugins/computer_use/inline_image.py +56 -0
  91. code_puppy_core_plugins/computer_use/keycodes.py +47 -0
  92. code_puppy_core_plugins/computer_use/policy.py +144 -0
  93. code_puppy_core_plugins/computer_use/register_callbacks.py +96 -0
  94. code_puppy_core_plugins/computer_use/safety.py +18 -0
  95. code_puppy_core_plugins/computer_use/settle.py +64 -0
  96. code_puppy_core_plugins/computer_use/state.py +86 -0
  97. code_puppy_core_plugins/computer_use/tools.py +469 -0
  98. code_puppy_core_plugins/context_indicator/__init__.py +14 -0
  99. code_puppy_core_plugins/context_indicator/register_callbacks.py +249 -0
  100. code_puppy_core_plugins/context_indicator/usage.py +40 -0
  101. code_puppy_core_plugins/copilot_auth/__init__.py +11 -0
  102. code_puppy_core_plugins/copilot_auth/config.py +93 -0
  103. code_puppy_core_plugins/copilot_auth/reasoning_client.py +409 -0
  104. code_puppy_core_plugins/copilot_auth/register_callbacks.py +460 -0
  105. code_puppy_core_plugins/copilot_auth/utils.py +587 -0
  106. code_puppy_core_plugins/customizable_commands/__init__.py +0 -0
  107. code_puppy_core_plugins/customizable_commands/register_callbacks.py +464 -0
  108. code_puppy_core_plugins/dbos_durable_exec/__init__.py +1 -0
  109. code_puppy_core_plugins/dbos_durable_exec/cancel.py +15 -0
  110. code_puppy_core_plugins/dbos_durable_exec/commands.py +31 -0
  111. code_puppy_core_plugins/dbos_durable_exec/config.py +31 -0
  112. code_puppy_core_plugins/dbos_durable_exec/lifecycle.py +83 -0
  113. code_puppy_core_plugins/dbos_durable_exec/register_callbacks.py +45 -0
  114. code_puppy_core_plugins/dbos_durable_exec/runtime.py +65 -0
  115. code_puppy_core_plugins/dbos_durable_exec/startup_lock.py +155 -0
  116. code_puppy_core_plugins/dbos_durable_exec/workflow_ids.py +16 -0
  117. code_puppy_core_plugins/dbos_durable_exec/wrapper.py +58 -0
  118. code_puppy_core_plugins/destructive_command_guard/AGENTS.md +79 -0
  119. code_puppy_core_plugins/destructive_command_guard/__init__.py +14 -0
  120. code_puppy_core_plugins/destructive_command_guard/detector.py +374 -0
  121. code_puppy_core_plugins/destructive_command_guard/register_callbacks.py +165 -0
  122. code_puppy_core_plugins/emoji_filter/__init__.py +12 -0
  123. code_puppy_core_plugins/emoji_filter/config.py +26 -0
  124. code_puppy_core_plugins/emoji_filter/register_callbacks.py +238 -0
  125. code_puppy_core_plugins/emoji_filter/stripper.py +45 -0
  126. code_puppy_core_plugins/empty_html_comment_filter/register_callbacks.py +116 -0
  127. code_puppy_core_plugins/example_custom_command/README.md +328 -0
  128. code_puppy_core_plugins/example_custom_command/register_callbacks.py +66 -0
  129. code_puppy_core_plugins/file_permission_handler/__init__.py +4 -0
  130. code_puppy_core_plugins/file_permission_handler/register_callbacks.py +575 -0
  131. code_puppy_core_plugins/flux_bootstrap/__init__.py +5 -0
  132. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/README.md +173 -0
  133. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/cheatsheet.md +86 -0
  134. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/pipeline.md +167 -0
  135. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/synopsis.md +392 -0
  136. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/about.md +51 -0
  137. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/address-feedback.md +129 -0
  138. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/ask.md +211 -0
  139. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/aug.md +181 -0
  140. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/auto-pilot.md +71 -0
  141. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/cheatsheet.md +16 -0
  142. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/commit.md +103 -0
  143. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/config.md +108 -0
  144. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/create-pr.md +195 -0
  145. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/exec.md +221 -0
  146. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/new.md +151 -0
  147. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/qa.md +208 -0
  148. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/rebase.md +180 -0
  149. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/review.md +304 -0
  150. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/split.md +120 -0
  151. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/squash-commits.md +179 -0
  152. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/status.md +46 -0
  153. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/tests.md +155 -0
  154. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_about.py +154 -0
  155. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_cheatsheet.py +248 -0
  156. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_status.py +345 -0
  157. code_puppy_core_plugins/flux_bootstrap/installer.py +256 -0
  158. code_puppy_core_plugins/flux_bootstrap/register_callbacks.py +71 -0
  159. code_puppy_core_plugins/force_push_guard/__init__.py +5 -0
  160. code_puppy_core_plugins/force_push_guard/detector.py +96 -0
  161. code_puppy_core_plugins/force_push_guard/register_callbacks.py +161 -0
  162. code_puppy_core_plugins/fork/__init__.py +7 -0
  163. code_puppy_core_plugins/fork/register_callbacks.py +445 -0
  164. code_puppy_core_plugins/frontend_emitter/__init__.py +25 -0
  165. code_puppy_core_plugins/frontend_emitter/emitter.py +249 -0
  166. code_puppy_core_plugins/frontend_emitter/register_callbacks.py +412 -0
  167. code_puppy_core_plugins/frontend_emitter/session_context.py +50 -0
  168. code_puppy_core_plugins/grok_oauth/__init__.py +6 -0
  169. code_puppy_core_plugins/grok_oauth/config.py +49 -0
  170. code_puppy_core_plugins/grok_oauth/oauth_flow.py +213 -0
  171. code_puppy_core_plugins/grok_oauth/register_callbacks.py +132 -0
  172. code_puppy_core_plugins/grok_oauth/utils.py +195 -0
  173. code_puppy_core_plugins/herdr/README.md +168 -0
  174. code_puppy_core_plugins/herdr/__init__.py +9 -0
  175. code_puppy_core_plugins/herdr/client.py +339 -0
  176. code_puppy_core_plugins/herdr/register_callbacks.py +140 -0
  177. code_puppy_core_plugins/herdr/reporter.py +212 -0
  178. code_puppy_core_plugins/herdr/smoke.py +143 -0
  179. code_puppy_core_plugins/herdr/sources.py +133 -0
  180. code_puppy_core_plugins/hook_creator/__init__.py +1 -0
  181. code_puppy_core_plugins/hook_creator/register_callbacks.py +33 -0
  182. code_puppy_core_plugins/hook_manager/__init__.py +1 -0
  183. code_puppy_core_plugins/hook_manager/config.py +290 -0
  184. code_puppy_core_plugins/hook_manager/hooks_menu.py +571 -0
  185. code_puppy_core_plugins/hook_manager/register_callbacks.py +227 -0
  186. code_puppy_core_plugins/meta_oauth/__init__.py +1 -0
  187. code_puppy_core_plugins/meta_oauth/config.py +55 -0
  188. code_puppy_core_plugins/meta_oauth/oauth_flow.py +109 -0
  189. code_puppy_core_plugins/meta_oauth/register_callbacks.py +154 -0
  190. code_puppy_core_plugins/meta_oauth/utils.py +252 -0
  191. code_puppy_core_plugins/namespace_skill_search/README.md +118 -0
  192. code_puppy_core_plugins/namespace_skill_search/__init__.py +15 -0
  193. code_puppy_core_plugins/namespace_skill_search/namespaces.py +125 -0
  194. code_puppy_core_plugins/namespace_skill_search/register_callbacks.py +99 -0
  195. code_puppy_core_plugins/namespace_skill_search/search_tool.py +158 -0
  196. code_puppy_core_plugins/no_tools/__init__.py +0 -0
  197. code_puppy_core_plugins/no_tools/register_callbacks.py +42 -0
  198. code_puppy_core_plugins/oauth_pasteback.py +110 -0
  199. code_puppy_core_plugins/oauth_puppy_html.py +194 -0
  200. code_puppy_core_plugins/obsidian_agent/README.md +52 -0
  201. code_puppy_core_plugins/obsidian_agent/__init__.py +8 -0
  202. code_puppy_core_plugins/obsidian_agent/agent_obsidian.py +147 -0
  203. code_puppy_core_plugins/obsidian_agent/register_callbacks.py +13 -0
  204. code_puppy_core_plugins/ollama/__init__.py +1 -0
  205. code_puppy_core_plugins/ollama/register_callbacks.py +127 -0
  206. code_puppy_core_plugins/ollama_setup/__init__.py +5 -0
  207. code_puppy_core_plugins/ollama_setup/completer.py +38 -0
  208. code_puppy_core_plugins/ollama_setup/register_callbacks.py +419 -0
  209. code_puppy_core_plugins/plugin_list/__init__.py +0 -0
  210. code_puppy_core_plugins/plugin_list/plugin_contributions.py +348 -0
  211. code_puppy_core_plugins/plugin_list/plugin_meta.py +67 -0
  212. code_puppy_core_plugins/plugin_list/plugin_text_utils.py +173 -0
  213. code_puppy_core_plugins/plugin_list/plugins_menu.py +455 -0
  214. code_puppy_core_plugins/plugin_list/plugins_menu_layout.py +194 -0
  215. code_puppy_core_plugins/plugin_list/plugins_menu_render.py +431 -0
  216. code_puppy_core_plugins/plugin_list/project_trust_flow.py +162 -0
  217. code_puppy_core_plugins/plugin_list/register_callbacks.py +281 -0
  218. code_puppy_core_plugins/pop_command/__init__.py +1 -0
  219. code_puppy_core_plugins/pop_command/register_callbacks.py +191 -0
  220. code_puppy_core_plugins/prompt_newline/__init__.py +13 -0
  221. code_puppy_core_plugins/prompt_newline/config.py +21 -0
  222. code_puppy_core_plugins/prompt_newline/register_callbacks.py +163 -0
  223. code_puppy_core_plugins/prune/__init__.py +1 -0
  224. code_puppy_core_plugins/prune/prune_menu.py +308 -0
  225. code_puppy_core_plugins/prune/prune_model.py +517 -0
  226. code_puppy_core_plugins/prune/prune_render.py +354 -0
  227. code_puppy_core_plugins/prune/register_callbacks.py +388 -0
  228. code_puppy_core_plugins/puppy_kennel/README.md +203 -0
  229. code_puppy_core_plugins/puppy_kennel/__init__.py +6 -0
  230. code_puppy_core_plugins/puppy_kennel/commands.py +206 -0
  231. code_puppy_core_plugins/puppy_kennel/config.py +36 -0
  232. code_puppy_core_plugins/puppy_kennel/kennel.py +368 -0
  233. code_puppy_core_plugins/puppy_kennel/packer.py +152 -0
  234. code_puppy_core_plugins/puppy_kennel/recorder.py +83 -0
  235. code_puppy_core_plugins/puppy_kennel/register_callbacks.py +109 -0
  236. code_puppy_core_plugins/puppy_kennel/retriever.py +28 -0
  237. code_puppy_core_plugins/puppy_kennel/schema.py +78 -0
  238. code_puppy_core_plugins/puppy_kennel/state.py +68 -0
  239. code_puppy_core_plugins/puppy_kennel/tools.py +449 -0
  240. code_puppy_core_plugins/puppy_kennel/wings.py +110 -0
  241. code_puppy_core_plugins/puppy_spinner/README.md +93 -0
  242. code_puppy_core_plugins/puppy_spinner/__init__.py +11 -0
  243. code_puppy_core_plugins/puppy_spinner/builtin_frames.py +172 -0
  244. code_puppy_core_plugins/puppy_spinner/commands.py +116 -0
  245. code_puppy_core_plugins/puppy_spinner/picker.py +379 -0
  246. code_puppy_core_plugins/puppy_spinner/register_callbacks.py +193 -0
  247. code_puppy_core_plugins/puppy_spinner/spinners.py +444 -0
  248. code_puppy_core_plugins/quick_resume/__init__.py +0 -0
  249. code_puppy_core_plugins/quick_resume/register_callbacks.py +77 -0
  250. code_puppy_core_plugins/review_pr/__init__.py +1 -0
  251. code_puppy_core_plugins/review_pr/register_callbacks.py +162 -0
  252. code_puppy_core_plugins/shell_safety/__init__.py +6 -0
  253. code_puppy_core_plugins/shell_safety/agent_shell_safety.py +69 -0
  254. code_puppy_core_plugins/shell_safety/command_cache.py +156 -0
  255. code_puppy_core_plugins/shell_safety/register_callbacks.py +203 -0
  256. code_puppy_core_plugins/stack_dump/__init__.py +1 -0
  257. code_puppy_core_plugins/stack_dump/register_callbacks.py +79 -0
  258. code_puppy_core_plugins/statusline/__init__.py +14 -0
  259. code_puppy_core_plugins/statusline/config.py +68 -0
  260. code_puppy_core_plugins/statusline/payload.py +138 -0
  261. code_puppy_core_plugins/statusline/prompt_patch.py +76 -0
  262. code_puppy_core_plugins/statusline/register_callbacks.py +45 -0
  263. code_puppy_core_plugins/statusline/runner.py +122 -0
  264. code_puppy_core_plugins/statusline/statusline_command.py +197 -0
  265. code_puppy_core_plugins/steer_queue/__init__.py +10 -0
  266. code_puppy_core_plugins/steer_queue/queue_menu.py +444 -0
  267. code_puppy_core_plugins/steer_queue/register_callbacks.py +138 -0
  268. code_puppy_core_plugins/subagent_panel/README.md +102 -0
  269. code_puppy_core_plugins/subagent_panel/__init__.py +24 -0
  270. code_puppy_core_plugins/subagent_panel/coalesce_patch.py +242 -0
  271. code_puppy_core_plugins/subagent_panel/panel_render.py +145 -0
  272. code_puppy_core_plugins/subagent_panel/register_callbacks.py +535 -0
  273. code_puppy_core_plugins/subagent_panel/resume_repaint.py +112 -0
  274. code_puppy_core_plugins/subagent_panel/state.py +231 -0
  275. code_puppy_core_plugins/switch_agent_resume/__init__.py +5 -0
  276. code_puppy_core_plugins/switch_agent_resume/register_callbacks.py +314 -0
  277. code_puppy_core_plugins/theme/README.md +86 -0
  278. code_puppy_core_plugins/theme/__init__.py +14 -0
  279. code_puppy_core_plugins/theme/bundled_palettes.py +346 -0
  280. code_puppy_core_plugins/theme/content_styles.py +146 -0
  281. code_puppy_core_plugins/theme/osc_palette.py +178 -0
  282. code_puppy_core_plugins/theme/picker.py +358 -0
  283. code_puppy_core_plugins/theme/prompt_toolkit_theme.py +113 -0
  284. code_puppy_core_plugins/theme/register_callbacks.py +348 -0
  285. code_puppy_core_plugins/theme/rich_themes.py +202 -0
  286. code_puppy_core_plugins/theme/themes.py +720 -0
  287. code_puppy_core_plugins/timestamp_heartbeat/__init__.py +1 -0
  288. code_puppy_core_plugins/timestamp_heartbeat/register_callbacks.py +114 -0
  289. code_puppy_core_plugins/token_ratio_learner/__init__.py +5 -0
  290. code_puppy_core_plugins/token_ratio_learner/ratios.py +208 -0
  291. code_puppy_core_plugins/token_ratio_learner/register_callbacks.py +213 -0
  292. code_puppy_core_plugins/universal_constructor/__init__.py +13 -0
  293. code_puppy_core_plugins/universal_constructor/models.py +138 -0
  294. code_puppy_core_plugins/universal_constructor/provider.py +62 -0
  295. code_puppy_core_plugins/universal_constructor/register_callbacks.py +75 -0
  296. code_puppy_core_plugins/universal_constructor/registry.py +302 -0
  297. code_puppy_core_plugins/universal_constructor/sandbox.py +584 -0
  298. code_puppy_core_plugins/wide_completion_menu/__init__.py +6 -0
  299. code_puppy_core_plugins/wide_completion_menu/register_callbacks.py +165 -0
  300. code_puppy_core_plugins/wiggum/__init__.py +1 -0
  301. code_puppy_core_plugins/wiggum/goal_runs.py +91 -0
  302. code_puppy_core_plugins/wiggum/judge.py +299 -0
  303. code_puppy_core_plugins/wiggum/judge_config.py +249 -0
  304. code_puppy_core_plugins/wiggum/judges_menu.py +814 -0
  305. code_puppy_core_plugins/wiggum/register_callbacks.py +569 -0
  306. code_puppy_core_plugins/wiggum/state.py +89 -0
  307. code_puppy_core_plugins/yolo_cli/__init__.py +1 -0
  308. code_puppy_core_plugins/yolo_cli/register_callbacks.py +41 -0
  309. code_puppy_core_plugins-0.0.2.dist-info/METADATA +64 -0
  310. code_puppy_core_plugins-0.0.2.dist-info/RECORD +313 -0
  311. code_puppy_core_plugins-0.0.2.dist-info/WHEEL +4 -0
  312. code_puppy_core_plugins-0.0.2.dist-info/entry_points.txt +55 -0
  313. code_puppy_core_plugins-0.0.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,137 @@
1
+ """
2
+ Configuration loader for Claude Code hooks.
3
+
4
+ Loads and merges hooks from multiple locations:
5
+ 1. ~/.code_puppy/hooks.json (global level) - always loaded if exists
6
+ 2. .claude/settings.json (project-level) - merged with global
7
+
8
+ Both configurations are loaded and merged so that hooks from both levels
9
+ coexist and execute together.
10
+ """
11
+
12
+ import json
13
+ import logging
14
+ import os
15
+ from pathlib import Path
16
+ from typing import Any, Dict, Optional
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ PROJECT_HOOKS_FILE = ".claude/settings.json"
21
+ GLOBAL_HOOKS_FILE = os.path.expanduser("~/.code_puppy/hooks.json")
22
+
23
+
24
+ def _deep_merge_hooks(base: Dict[str, Any], overlay: Dict[str, Any]) -> Dict[str, Any]:
25
+ """
26
+ Merge hook configurations, combining event types and hook groups.
27
+
28
+ When the same event type exists in both base and overlay, their hook groups
29
+ are concatenated (overlay hooks appear after base hooks).
30
+
31
+ Args:
32
+ base: Base configuration dictionary
33
+ overlay: Configuration to merge on top
34
+
35
+ Returns:
36
+ Merged configuration with all hooks from both sources
37
+ """
38
+ merged = dict(base)
39
+
40
+ for event_type, hook_groups in overlay.items():
41
+ if event_type.startswith("_"):
42
+ # Skip comment keys
43
+ merged[event_type] = hook_groups
44
+ continue
45
+
46
+ if event_type not in merged:
47
+ # New event type, just add it
48
+ merged[event_type] = hook_groups
49
+ elif isinstance(merged[event_type], list) and isinstance(hook_groups, list):
50
+ # Both are lists, concatenate them (overlay hooks come after)
51
+ merged[event_type] = merged[event_type] + hook_groups
52
+ logger.debug(
53
+ f"Merged {len(hook_groups)} hook group(s) for event '{event_type}'"
54
+ )
55
+ else:
56
+ # Type mismatch or unexpected structure, keep base
57
+ logger.warning(
58
+ f"Cannot merge event type '{event_type}': type mismatch or unexpected structure"
59
+ )
60
+
61
+ return merged
62
+
63
+
64
+ def load_hooks_config() -> Optional[Dict[str, Any]]:
65
+ """
66
+ Load and merge hooks configuration from available sources.
67
+
68
+ Priority order:
69
+ 1. ~/.code_puppy/hooks.json (global level) - always loaded if exists
70
+ 2. .claude/settings.json (project-level) - merged with global
71
+
72
+ Returns:
73
+ Configuration dictionary or None if no config found
74
+ """
75
+ merged_config: Dict[str, Any] = {}
76
+
77
+ # Load global hooks first
78
+ global_config_path = Path(GLOBAL_HOOKS_FILE)
79
+
80
+ if global_config_path.exists():
81
+ try:
82
+ with open(global_config_path, "r", encoding="utf-8") as f:
83
+ config = json.load(f)
84
+ if "hooks" in config and isinstance(config["hooks"], dict):
85
+ logger.info(
86
+ f"Loaded hooks configuration (wrapped format) from {GLOBAL_HOOKS_FILE}"
87
+ )
88
+ merged_config = _deep_merge_hooks(merged_config, config["hooks"])
89
+ elif isinstance(config, dict):
90
+ logger.info(f"Loaded hooks configuration from {GLOBAL_HOOKS_FILE}")
91
+ merged_config = _deep_merge_hooks(merged_config, config)
92
+ except json.JSONDecodeError as e:
93
+ logger.error(f"Invalid JSON in {GLOBAL_HOOKS_FILE}: {e}")
94
+ except Exception as e:
95
+ logger.error(f"Failed to load {GLOBAL_HOOKS_FILE}: {e}", exc_info=True)
96
+
97
+ # Load and merge project-level hooks
98
+ project_config_path = Path(os.getcwd()) / PROJECT_HOOKS_FILE
99
+
100
+ if project_config_path.exists():
101
+ try:
102
+ with open(project_config_path, "r", encoding="utf-8") as f:
103
+ config = json.load(f)
104
+ hooks_config = config.get("hooks")
105
+ if hooks_config:
106
+ logger.info(f"Merging hooks configuration from {project_config_path}")
107
+ merged_config = _deep_merge_hooks(merged_config, hooks_config)
108
+ else:
109
+ logger.debug(f"No 'hooks' section found in {project_config_path}")
110
+ except json.JSONDecodeError as e:
111
+ logger.error(f"Invalid JSON in {project_config_path}: {e}")
112
+ except Exception as e:
113
+ logger.error(f"Failed to load {project_config_path}: {e}", exc_info=True)
114
+
115
+ if not merged_config:
116
+ logger.debug("No hooks configuration found")
117
+ return None
118
+
119
+ event_count = len(
120
+ [event for event in merged_config.keys() if not event.startswith("_")]
121
+ )
122
+ logger.info(f"Hooks configuration ready ({event_count} event type(s))")
123
+ return merged_config
124
+
125
+
126
+ def get_hooks_config_paths() -> list:
127
+ """
128
+ Return list of hook configuration paths.
129
+
130
+ Returns paths in order of precedence (project-level first, then global).
131
+ Note: internally, hooks are loaded in reverse order (global first, then project)
132
+ so that project-level hooks can extend/append to global hooks.
133
+ """
134
+ return [
135
+ str(Path(os.getcwd()) / PROJECT_HOOKS_FILE),
136
+ GLOBAL_HOOKS_FILE,
137
+ ]
@@ -0,0 +1,383 @@
1
+ """
2
+ Register callbacks for Claude Code hooks plugin.
3
+
4
+ Integrates the hook engine with code_puppy's callback system.
5
+
6
+ This bridge maps Claude Code hook events to code_puppy lifecycle callbacks:
7
+
8
+ Claude Code event → code_puppy callback
9
+ ----------------- → -------------------
10
+ PreToolUse → pre_tool_call
11
+ PostToolUse → post_tool_call
12
+ SessionStart → startup
13
+ SessionEnd → session_end
14
+ UserPromptSubmit → user_prompt_submit
15
+ PreCompact → pre_compact
16
+ Notification → notification
17
+ Stop / SubagentStop → agent_run_end
18
+
19
+ Hook stdout on exit code 0 is propagated to the agent context for the events
20
+ where Claude Code's spec says it should become "additional context"
21
+ (SessionStart, UserPromptSubmit, PreToolUse). See issue #298.
22
+ """
23
+
24
+ import logging
25
+ from typing import Any, Dict, List, Optional
26
+
27
+ from code_puppy.callbacks import register_callback
28
+ from code_puppy.hook_engine import EventData, HookEngine
29
+
30
+ from .config import load_hooks_config
31
+
32
+ _SUBAGENT_NAMES = frozenset(
33
+ {
34
+ "pack_leader",
35
+ "bloodhound",
36
+ "code-puppy",
37
+ "code_puppy",
38
+ "retriever",
39
+ "shepherd",
40
+ "terrier",
41
+ "watchdog",
42
+ "subagent",
43
+ "sub_agent",
44
+ }
45
+ )
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+ _hook_engine: Optional[HookEngine] = None
50
+
51
+ # Deferred-context buffer: SessionStart hook stdout lands in the next user prompt
52
+ # (where the spec says "additional context" goes). One-shot per session.
53
+ _pending_session_context: List[str] = []
54
+
55
+
56
+ def _initialize_engine() -> Optional[HookEngine]:
57
+ config = load_hooks_config()
58
+
59
+ if not config:
60
+ logger.info("No hooks configuration found - Claude Code hooks disabled")
61
+ return None
62
+
63
+ try:
64
+ engine = HookEngine(config, strict_validation=False)
65
+ stats = engine.get_stats()
66
+ logger.info(
67
+ f"Hook engine ready - Total: {stats['total_hooks']}, "
68
+ f"Enabled: {stats['enabled_hooks']}"
69
+ )
70
+ return engine
71
+ except Exception as e:
72
+ logger.error(f"Failed to initialize hook engine: {e}", exc_info=True)
73
+ return None
74
+
75
+
76
+ _hook_engine = _initialize_engine()
77
+
78
+
79
+ def _collect_context_stdout(result: Any) -> List[str]:
80
+ """Pull stdout from non-blocking, exit-0 hook results.
81
+
82
+ Per Claude Code spec, only exit code 0 hooks contribute "additional
83
+ context" — exit 1 blocks and exit 2 routes stderr back as a tool error.
84
+ """
85
+ chunks: List[str] = []
86
+ for r in getattr(result, "results", []) or []:
87
+ if getattr(r, "blocked", False):
88
+ continue
89
+ if getattr(r, "exit_code", 0) != 0:
90
+ continue
91
+ stdout = (getattr(r, "stdout", "") or "").strip()
92
+ if stdout:
93
+ chunks.append(stdout)
94
+ return chunks
95
+
96
+
97
+ # ---------------------------------------------------------------------------
98
+ # PreToolUse / PostToolUse
99
+ # ---------------------------------------------------------------------------
100
+
101
+
102
+ async def on_pre_tool_call_hook(
103
+ tool_name: str,
104
+ tool_args: Dict[str, Any],
105
+ context: Any = None,
106
+ ) -> Optional[Dict[str, Any]]:
107
+ """Pre-tool callback — executes hooks before tool runs. Can block AND
108
+ inject stdout as additional context for the model."""
109
+ if not _hook_engine:
110
+ return None
111
+
112
+ event_data = EventData(
113
+ event_type="PreToolUse",
114
+ tool_name=tool_name,
115
+ tool_args=tool_args,
116
+ context={"context": context} if context else {},
117
+ )
118
+
119
+ try:
120
+ result = await _hook_engine.process_event("PreToolUse", event_data)
121
+
122
+ if result.blocked:
123
+ logger.debug(
124
+ f"Tool '{tool_name}' blocked by hook: {result.blocking_reason}"
125
+ )
126
+ return {
127
+ "blocked": True,
128
+ "reason": result.blocking_reason,
129
+ "error_message": result.blocking_reason,
130
+ }
131
+
132
+ # Exit code 0 hooks: propagate stdout via ``context_message`` (issue #298) so
133
+ # the pydantic_patches consumer prepends it to the tool result.
134
+ stdout_chunks = _collect_context_stdout(result)
135
+ if stdout_chunks:
136
+ return {"context_message": "\n\n".join(stdout_chunks)}
137
+ return None
138
+ except Exception as e:
139
+ logger.error(f"Error in pre-tool hook: {e}", exc_info=True)
140
+ return None
141
+
142
+
143
+ async def on_post_tool_call_hook(
144
+ tool_name: str,
145
+ tool_args: Dict[str, Any],
146
+ result: Any,
147
+ duration_ms: float,
148
+ context: Any = None,
149
+ ) -> None:
150
+ """Post-tool callback — executes hooks after tool completes."""
151
+ if not _hook_engine:
152
+ return
153
+
154
+ event_data = EventData(
155
+ event_type="PostToolUse",
156
+ tool_name=tool_name,
157
+ tool_args=tool_args,
158
+ context={"result": result, "duration_ms": duration_ms, "context": context},
159
+ )
160
+
161
+ try:
162
+ await _hook_engine.process_event("PostToolUse", event_data)
163
+ except Exception as e:
164
+ logger.error(f"Error in post-tool hook: {e}", exc_info=True)
165
+
166
+
167
+ register_callback("pre_tool_call", on_pre_tool_call_hook)
168
+ register_callback("post_tool_call", on_post_tool_call_hook)
169
+
170
+
171
+ # ---------------------------------------------------------------------------
172
+ # SessionStart / SessionEnd
173
+ # ---------------------------------------------------------------------------
174
+
175
+
176
+ async def on_startup_hook() -> None:
177
+ """Startup callback — fires SessionStart hooks when code_puppy boots.
178
+
179
+ Captures stdout into ``_pending_session_context`` so the first user prompt
180
+ can be augmented with the SessionStart "additional context" (project
181
+ constitutions, etc.). See issue #298.
182
+ """
183
+ if not _hook_engine:
184
+ return
185
+
186
+ event_data = EventData(
187
+ event_type="SessionStart",
188
+ tool_name="session",
189
+ tool_args={},
190
+ )
191
+
192
+ try:
193
+ result = await _hook_engine.process_event("SessionStart", event_data)
194
+ stdout_chunks = _collect_context_stdout(result)
195
+ if stdout_chunks:
196
+ _pending_session_context.extend(stdout_chunks)
197
+ logger.debug(
198
+ f"SessionStart captured {len(stdout_chunks)} stdout chunk(s) "
199
+ f"for injection on next user prompt"
200
+ )
201
+ except Exception as e:
202
+ logger.error(f"Error in SessionStart hook: {e}", exc_info=True)
203
+
204
+
205
+ async def on_session_end_hook() -> None:
206
+ """Session-end callback — fires SessionEnd hooks (issue #298)."""
207
+ if not _hook_engine:
208
+ return
209
+
210
+ event_data = EventData(
211
+ event_type="SessionEnd",
212
+ tool_name="session",
213
+ tool_args={},
214
+ )
215
+
216
+ try:
217
+ await _hook_engine.process_event("SessionEnd", event_data)
218
+ except Exception as e:
219
+ logger.error(f"Error in SessionEnd hook: {e}", exc_info=True)
220
+
221
+
222
+ register_callback("startup", on_startup_hook)
223
+ register_callback("session_end", on_session_end_hook)
224
+
225
+
226
+ # ---------------------------------------------------------------------------
227
+ # UserPromptSubmit
228
+ # ---------------------------------------------------------------------------
229
+
230
+
231
+ async def on_user_prompt_submit_hook(
232
+ prompt: str, session_id: Optional[str] = None
233
+ ) -> Optional[str]:
234
+ """Fire UserPromptSubmit hooks and inject their stdout (+ any pending
235
+ SessionStart stdout) into the user prompt.
236
+
237
+ Returns the (possibly augmented) prompt, or ``None`` if there's nothing
238
+ to add. See issue #298.
239
+ """
240
+ chunks: List[str] = []
241
+
242
+ # Drain any pending SessionStart context first.
243
+ if _pending_session_context:
244
+ chunks.extend(_pending_session_context)
245
+ _pending_session_context.clear()
246
+
247
+ if _hook_engine:
248
+ event_data = EventData(
249
+ event_type="UserPromptSubmit",
250
+ tool_name="user_prompt",
251
+ tool_args={"prompt": prompt},
252
+ context={"session_id": session_id} if session_id else {},
253
+ )
254
+
255
+ try:
256
+ result = await _hook_engine.process_event("UserPromptSubmit", event_data)
257
+ chunks.extend(_collect_context_stdout(result))
258
+ except Exception as e:
259
+ logger.error(f"Error in UserPromptSubmit hook: {e}", exc_info=True)
260
+
261
+ if not chunks:
262
+ return None
263
+
264
+ header = "\n\n".join(f"[hook context]\n{c}" for c in chunks)
265
+ return f"{header}\n\n{prompt}"
266
+
267
+
268
+ register_callback("user_prompt_submit", on_user_prompt_submit_hook)
269
+
270
+
271
+ # ---------------------------------------------------------------------------
272
+ # PreCompact
273
+ # ---------------------------------------------------------------------------
274
+
275
+
276
+ async def on_pre_compact_hook(
277
+ agent_name: str,
278
+ strategy: str,
279
+ message_count: int,
280
+ token_count: int,
281
+ ) -> None:
282
+ """Fire PreCompact hooks before history compaction (issue #298)."""
283
+ if not _hook_engine:
284
+ return
285
+
286
+ event_data = EventData(
287
+ event_type="PreCompact",
288
+ tool_name="compact",
289
+ tool_args={
290
+ "agent_name": agent_name,
291
+ "strategy": strategy,
292
+ "message_count": message_count,
293
+ "token_count": token_count,
294
+ },
295
+ )
296
+
297
+ try:
298
+ await _hook_engine.process_event("PreCompact", event_data)
299
+ except Exception as e:
300
+ logger.error(f"Error in PreCompact hook: {e}", exc_info=True)
301
+
302
+
303
+ register_callback("pre_compact", on_pre_compact_hook)
304
+
305
+
306
+ # ---------------------------------------------------------------------------
307
+ # Notification
308
+ # ---------------------------------------------------------------------------
309
+
310
+
311
+ async def on_notification_hook(
312
+ message: str, level: str = "info", context: Any = None
313
+ ) -> None:
314
+ """Fire Notification hooks when the agent surfaces a user-attention event."""
315
+ if not _hook_engine:
316
+ return
317
+
318
+ event_data = EventData(
319
+ event_type="Notification",
320
+ tool_name="notification",
321
+ tool_args={"message": message, "level": level},
322
+ context={"context": context} if context else {},
323
+ )
324
+
325
+ try:
326
+ await _hook_engine.process_event("Notification", event_data)
327
+ except Exception as e:
328
+ logger.error(f"Error in Notification hook: {e}", exc_info=True)
329
+
330
+
331
+ register_callback("notification", on_notification_hook)
332
+
333
+
334
+ # ---------------------------------------------------------------------------
335
+ # Stop / SubagentStop (via agent_run_end)
336
+ # ---------------------------------------------------------------------------
337
+
338
+
339
+ async def on_agent_run_end_hook(
340
+ agent_name: str,
341
+ model_name: str,
342
+ session_id: str | None = None,
343
+ success: bool = True,
344
+ error: Exception | None = None,
345
+ response_text: str | None = None,
346
+ metadata: dict | None = None,
347
+ ) -> None:
348
+ """agent_run_end callback — fires Stop or SubagentStop hooks."""
349
+ if not _hook_engine:
350
+ return
351
+
352
+ agent_lower = (agent_name or "").lower()
353
+ is_subagent = any(name in agent_lower for name in _SUBAGENT_NAMES)
354
+ event_type = "SubagentStop" if is_subagent else "Stop"
355
+
356
+ event_data = EventData(
357
+ event_type=event_type,
358
+ tool_name=agent_name or "agent",
359
+ tool_args={},
360
+ context={
361
+ "agent_name": agent_name,
362
+ "model_name": model_name,
363
+ "session_id": session_id,
364
+ "success": success,
365
+ "error": str(error) if error else None,
366
+ "response_text": response_text,
367
+ "metadata": metadata,
368
+ },
369
+ )
370
+
371
+ try:
372
+ await _hook_engine.process_event(event_type, event_data)
373
+ except Exception as e:
374
+ logger.error(f"Error in {event_type} hook: {e}", exc_info=True)
375
+
376
+
377
+ register_callback("agent_run_end", on_agent_run_end_hook)
378
+
379
+ logger.info(
380
+ "Claude Code hooks plugin registered (PreToolUse, PostToolUse, "
381
+ "SessionStart, SessionEnd, UserPromptSubmit, PreCompact, "
382
+ "Notification, Stop, SubagentStop)"
383
+ )
@@ -0,0 +1,167 @@
1
+ # Claude Code OAuth Plugin
2
+
3
+ This plugin adds OAuth authentication for Claude Code to Code Puppy, automatically importing available models into your configuration.
4
+
5
+ ## Features
6
+
7
+ - **OAuth Authentication**: Secure OAuth flow for Claude Code using PKCE
8
+ - **Automatic Model Discovery**: Fetches available models from the Claude API once authenticated
9
+ - **Model Registration**: Automatically adds models to `extra_models.json` with the `claude-code-` prefix
10
+ - **Token Management**: Secure storage of OAuth tokens in the Code Puppy config directory
11
+ - **Browser Integration**: Launches the Claude OAuth consent flow automatically
12
+ - **Callback Capture**: Listens on localhost to receive and process the OAuth redirect
13
+
14
+ ## Commands
15
+
16
+ ### `/claude-code-auth`
17
+ Authenticate with Claude Code via OAuth and import available models.
18
+
19
+ This will:
20
+ 1. Launch the Claude OAuth consent flow in your browser
21
+ 2. Walk you through approving access for the shared `claude-cli` client
22
+ 3. Capture the redirect from Claude in a temporary local callback server
23
+ 4. Exchange the returned code for access tokens and store them securely
24
+ 5. Fetch available models from Claude Code and add them to your configuration
25
+
26
+ ### `/claude-code-status`
27
+ Check Claude Code OAuth authentication status and configured models.
28
+
29
+ Shows:
30
+ - Current authentication status
31
+ - Token expiry information (if available)
32
+ - Number and names of configured Claude Code models
33
+
34
+ ### `/claude-code-logout`
35
+ Remove Claude Code OAuth tokens and imported models.
36
+
37
+ This will:
38
+ 1. Remove stored OAuth tokens
39
+ 2. Remove all Claude Code models from `extra_models.json`
40
+
41
+ ## Setup
42
+
43
+ ### Prerequisites
44
+
45
+ 1. **Claude account** with access to the Claude Console developer settings
46
+ 2. **Browser access** to generate authorization codes
47
+
48
+ ### Configuration
49
+
50
+ The plugin ships with sensible defaults in `config.py`:
51
+
52
+ ```python
53
+ CLAUDE_CODE_OAUTH_CONFIG = {
54
+ "auth_url": "https://claude.ai/oauth/authorize",
55
+ "token_url": "https://console.anthropic.com/v1/oauth/token",
56
+ "api_base_url": "https://api.anthropic.com",
57
+ "client_id": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
58
+ "scope": "org:create_api_key user:profile user:inference",
59
+ "redirect_host": "http://localhost",
60
+ "redirect_path": "callback",
61
+ "callback_port_range": (8765, 8795),
62
+ "callback_timeout": 180,
63
+ "prefix": "claude-code-",
64
+ "default_context_length": 200000,
65
+ "api_key_env_var": "CLAUDE_CODE_ACCESS_TOKEN",
66
+ }
67
+ ```
68
+
69
+ These values mirror the public client used by llxprt-code. Adjust only if Anthropic changes their configuration.
70
+
71
+ ### Environment Variables
72
+
73
+ After authentication, the models will reference:
74
+ - `CLAUDE_CODE_ACCESS_TOKEN`: Automatically written by the plugin
75
+
76
+ ## Usage Example
77
+
78
+ ```bash
79
+ # Authenticate with Claude Code
80
+ /claude-code-auth
81
+
82
+ # Check status
83
+ /claude-code-status
84
+
85
+ # Use a Claude Code model
86
+ /set model claude-code-claude-3-5-sonnet-20241022
87
+
88
+ # When done, logout
89
+ /claude-code-logout
90
+ ```
91
+
92
+ ## Model Configuration
93
+
94
+ After authentication, models will be added to `~/.code_puppy/extra_models.json`:
95
+
96
+ ```json
97
+ {
98
+ "claude-code-claude-3-5-sonnet-20241022": {
99
+ "type": "anthropic",
100
+ "name": "claude-3-5-sonnet-20241022",
101
+ "custom_endpoint": {
102
+ "url": "https://api.anthropic.com",
103
+ "api_key": "$CLAUDE_CODE_ACCESS_TOKEN"
104
+ },
105
+ "context_length": 200000,
106
+ "oauth_source": "claude-code-plugin"
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Security
112
+
113
+ - **Token Storage**: Tokens are saved to `~/.code_puppy/claude_code_oauth.json` with `0o600` permissions
114
+ - **PKCE Support**: Uses Proof Key for Code Exchange for enhanced security
115
+ - **State Validation**: Checks the returned state (if provided) to guard against CSRF
116
+ - **HTTPS Only**: All OAuth communications use HTTPS endpoints
117
+
118
+ ## Troubleshooting
119
+
120
+ ### Browser doesn't open
121
+ - Manually visit the URL shown in the output
122
+ - Check that a default browser is configured
123
+
124
+ ### Authentication fails
125
+ - Ensure the browser completed the redirect back to Code Puppy (no pop-up blockers)
126
+ - Retry if the window shows an error; codes expire quickly
127
+ - Confirm network access to `claude.ai`
128
+
129
+ ### Models not showing up
130
+ - Claude may not return the model list for your account; verify access manually
131
+ - Check `/claude-code-status` to confirm authentication succeeded
132
+
133
+ ## Development
134
+
135
+ ### File Structure
136
+
137
+ ```
138
+ claude_code_oauth/
139
+ ├── __init__.py
140
+ ├── register_callbacks.py # Main plugin logic and command handlers
141
+ ├── config.py # Configuration settings
142
+ ├── utils.py # OAuth helpers and file operations
143
+ ├── README.md # This file
144
+ ├── SETUP.md # Quick setup guide
145
+ └── test_plugin.py # Manual test helper
146
+ ```
147
+
148
+ ### Key Components
149
+
150
+ - **OAuth Flow**: Authorization code flow with PKCE and automatic callback capture
151
+ - **Token Management**: Secure storage and retrieval helpers
152
+ - **Model Discovery**: API integration for model fetching
153
+ - **Plugin Registration**: Custom command handlers wired into Code Puppy
154
+
155
+ ## Notes
156
+
157
+ - The plugin assumes Anthropic continues to expose the shared `claude-cli` OAuth client
158
+ - Tokens are refreshed on subsequent API calls if the service returns refresh tokens
159
+ - Models are prefixed with `claude-code-` to avoid collisions with other Anthropic models
160
+
161
+ ## Contributing
162
+
163
+ When modifying this plugin:
164
+ 1. Maintain security best practices
165
+ 2. Test OAuth flow changes manually before shipping
166
+ 3. Update documentation for any configuration or UX changes
167
+ 4. Keep files under 600 lines; split into helpers when needed