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,449 @@
1
+ """Agent-facing tools for the puppy_kennel.
2
+
3
+ Full tool surface:
4
+
5
+ * ``kennel_recall`` — BM25 search across wings
6
+ * ``kennel_remember`` — explicit verbatim write to a chosen wing
7
+ * ``kennel_recent`` — recent drawers without a query (timeline view)
8
+ * ``kennel_list_wings`` — discover wings + drawer counts
9
+ * ``kennel_stats`` — kennel-wide totals + on-disk size
10
+
11
+ All tools share a single ``register_tools_callback`` plugin entrypoint.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Any
17
+
18
+ from pydantic import BaseModel, Field
19
+ from pydantic_ai import RunContext
20
+
21
+ from . import kennel
22
+ from .config import DB_PATH
23
+ from .state import DISABLED_TOOL_ERROR, is_enabled
24
+ from .wings import (
25
+ USER_WING,
26
+ agent_wing,
27
+ default_recall_scope,
28
+ detect_cwd,
29
+ repo_wing,
30
+ )
31
+
32
+
33
+ class KennelDrawer(BaseModel):
34
+ """A single recalled drawer in tool output form."""
35
+
36
+ id: int
37
+ room_id: int
38
+ role: str | None = None
39
+ content: str
40
+ ts: str
41
+ session_id: str | None = None
42
+ agent: str | None = None
43
+ cwd: str | None = None
44
+
45
+
46
+ class KennelRecallOutput(BaseModel):
47
+ """Output for ``kennel_recall``."""
48
+
49
+ query: str
50
+ wings_searched: list[str]
51
+ total_hits: int
52
+ drawers: list[KennelDrawer] = Field(default_factory=list)
53
+ error: str | None = None
54
+
55
+
56
+ class KennelRememberOutput(BaseModel):
57
+ """Output for ``kennel_remember``."""
58
+
59
+ drawer_id: int
60
+ wing: str
61
+ room: str
62
+ bytes_stored: int
63
+ error: str | None = None
64
+
65
+
66
+ class KennelRecentOutput(BaseModel):
67
+ """Output for ``kennel_recent``."""
68
+
69
+ wings_searched: list[str]
70
+ total: int
71
+ drawers: list[KennelDrawer] = Field(default_factory=list)
72
+ error: str | None = None
73
+
74
+
75
+ class WingInfo(BaseModel):
76
+ name: str
77
+ drawer_count: int
78
+
79
+
80
+ class KennelWingsOutput(BaseModel):
81
+ """Output for ``kennel_list_wings``."""
82
+
83
+ wings: list[WingInfo] = Field(default_factory=list)
84
+ total_wings: int
85
+ error: str | None = None
86
+
87
+
88
+ class KennelStatsOutput(BaseModel):
89
+ """Output for ``kennel_stats``."""
90
+
91
+ db_path: str
92
+ db_size_bytes: int
93
+ total_drawers: int
94
+ total_wings: int
95
+ error: str | None = None
96
+
97
+
98
+ def _drawer_to_model(d: Any) -> KennelDrawer:
99
+ meta = d.metadata or {}
100
+ return KennelDrawer(
101
+ id=d.id,
102
+ room_id=d.room_id,
103
+ role=d.role,
104
+ content=d.content,
105
+ ts=d.ts,
106
+ session_id=d.session_id,
107
+ agent=meta.get("agent"),
108
+ cwd=meta.get("cwd"),
109
+ )
110
+
111
+
112
+ def _resolve_wing(value: str, agent_name: str, cwd: Any) -> str:
113
+ """Turn a wing shortcut into a concrete wing name.
114
+
115
+ Shortcuts:
116
+ ``"repo"`` -> current repo wing (the default for writes)
117
+ ``"agent"`` -> this agent's diary wing
118
+ ``"user"`` -> the global user-prefs wing
119
+ ``""`` -> defaults to ``repo`` (most writes are project-scoped)
120
+ anything else is treated as an explicit wing name.
121
+ """
122
+ v = (value or "").strip()
123
+ if v == "" or v == "repo":
124
+ return repo_wing(cwd)
125
+ if v == "agent":
126
+ return agent_wing(agent_name)
127
+ if v == "user":
128
+ return USER_WING
129
+ return v
130
+
131
+
132
+ def _resolve_scope(wing: str, scope: str, agent_name: str, cwd: Any) -> list[str]:
133
+ """Turn (wing, scope) into the list of wings to search.
134
+
135
+ Returns an empty list to mean "no wing filter — search everything."
136
+ """
137
+ w = (wing or "").strip()
138
+ if w:
139
+ return [_resolve_wing(w, agent_name, cwd)]
140
+ if scope == "repo":
141
+ return [repo_wing(cwd)]
142
+ if scope == "agent":
143
+ return [agent_wing(agent_name)]
144
+ if scope == "user":
145
+ return [USER_WING]
146
+ if scope == "all":
147
+ return [] # caller treats empty as "no filter"
148
+ # "default" or anything weird
149
+ return default_recall_scope(agent_name, cwd)
150
+
151
+
152
+ def register_kennel_recall(agent: Any) -> None:
153
+ """Register the ``kennel_recall`` tool on the given agent."""
154
+
155
+ @agent.tool
156
+ async def kennel_recall(
157
+ context: RunContext,
158
+ query: str,
159
+ wing: str = "",
160
+ top_k: int = 5,
161
+ scope: str = "default",
162
+ ) -> KennelRecallOutput:
163
+ """Search the Puppy Kennel for relevant past drawers (verbatim memories).
164
+
165
+ The kennel stores prior agent responses scoped by wing:
166
+ * ``repo:<path>`` — project memory shared across agents in that repo
167
+ * ``agent:<name>`` — this agent's private diary
168
+ * ``user:default`` — cross-cutting user preferences
169
+
170
+ Args:
171
+ query: Free-form text. FTS5 BM25 ranks results.
172
+ wing: Restrict to a single wing (e.g. ``"repo:/Users/me/foo"``).
173
+ Leave blank to use ``scope``.
174
+ top_k: Number of drawers to return (default 5, max 20).
175
+ scope: When ``wing`` is empty, choose the wing set:
176
+ * ``"default"`` — repo + this agent + user (sensible default)
177
+ * ``"repo"`` — only the current repo wing
178
+ * ``"agent"`` — only this agent's diary
179
+ * ``"all"`` — every wing (use sparingly)
180
+
181
+ Returns drawers ordered by BM25 relevance, deduplicated by content.
182
+ """
183
+ if not is_enabled():
184
+ return KennelRecallOutput(
185
+ query=query or "",
186
+ wings_searched=[],
187
+ total_hits=0,
188
+ error=DISABLED_TOOL_ERROR,
189
+ )
190
+ try:
191
+ top_k = max(1, min(int(top_k), 20))
192
+ except (TypeError, ValueError):
193
+ top_k = 5
194
+
195
+ if not query or not query.strip():
196
+ return KennelRecallOutput(
197
+ query=query or "",
198
+ wings_searched=[],
199
+ total_hits=0,
200
+ error="Empty query — provide some text to search for.",
201
+ )
202
+
203
+ try:
204
+ agent_name = _agent_name_from_context(context)
205
+ cwd = detect_cwd()
206
+ wings_to_search = _resolve_scope(wing, scope, agent_name, cwd)
207
+
208
+ hits = kennel.search_drawers_multi(
209
+ query=query,
210
+ wing_names=wings_to_search or None,
211
+ limit=top_k,
212
+ )
213
+ return KennelRecallOutput(
214
+ query=query,
215
+ wings_searched=wings_to_search,
216
+ total_hits=len(hits),
217
+ drawers=[_drawer_to_model(d) for d in hits],
218
+ )
219
+ except Exception as exc: # noqa: BLE001 — tool must never raise.
220
+ return KennelRecallOutput(
221
+ query=query,
222
+ wings_searched=[],
223
+ total_hits=0,
224
+ error=f"kennel_recall failed: {exc!r}",
225
+ )
226
+
227
+
228
+ def register_kennel_remember(agent: Any) -> None:
229
+ """Register the ``kennel_remember`` tool — explicit verbatim write."""
230
+
231
+ @agent.tool
232
+ async def kennel_remember(
233
+ context: RunContext,
234
+ content: str,
235
+ wing: str = "repo",
236
+ room: str = "notes",
237
+ ) -> KennelRememberOutput:
238
+ """Save a verbatim note to the Puppy Kennel.
239
+
240
+ Use this when the user says "remember that..." or when you learn
241
+ something durable that future sessions should know about. Writes
242
+ are best-effort; failures return an error string rather than raising.
243
+
244
+ **Pick the wing deliberately — it determines who sees this memory:**
245
+
246
+ * ``"repo"`` (default) — facts about THIS project: architectural
247
+ decisions, gotchas, conventions, why-we-chose-X. Anyone working
248
+ in this repo will see it. Use this for most "remember that..."
249
+ requests during project work.
250
+ * ``"user"`` — facts about the user (Mike): preferences, style,
251
+ biographical detail. Pervasive across every repo and every
252
+ agent. Use sparingly — only for cross-cutting truths.
253
+ * ``"agent"`` — cross-project learnings about your own behavior
254
+ or limitations. Rare. Most "agent" notes are actually project
255
+ notes wearing a costume — prefer ``"repo"`` unless the lesson
256
+ genuinely transcends projects.
257
+
258
+ Args:
259
+ content: The verbatim text to remember. Required.
260
+ wing: ``"repo"`` (default) / ``"user"`` / ``"agent"`` /
261
+ or an explicit wing name like ``"team:platform"``.
262
+ room: Room name within the wing. Defaults to ``"notes"``.
263
+ Use ``"preferences"`` for user-wing writes,
264
+ ``"decisions"`` for project-wide architectural calls.
265
+ """
266
+ if not is_enabled():
267
+ return KennelRememberOutput(
268
+ drawer_id=0,
269
+ wing="",
270
+ room=room,
271
+ bytes_stored=0,
272
+ error=DISABLED_TOOL_ERROR,
273
+ )
274
+ if not content or not content.strip():
275
+ return KennelRememberOutput(
276
+ drawer_id=0,
277
+ wing="",
278
+ room=room,
279
+ bytes_stored=0,
280
+ error="Empty content — nothing to remember.",
281
+ )
282
+ try:
283
+ agent_name = _agent_name_from_context(context)
284
+ cwd = detect_cwd()
285
+ resolved_wing = _resolve_wing(wing, agent_name, cwd)
286
+ drawer_id = kennel.write_note(
287
+ wing_name=resolved_wing,
288
+ room_name=(room or "notes").strip() or "notes",
289
+ content=content,
290
+ role="note",
291
+ metadata={"agent": agent_name, "cwd": str(cwd), "explicit": True},
292
+ )
293
+ return KennelRememberOutput(
294
+ drawer_id=drawer_id,
295
+ wing=resolved_wing,
296
+ room=room or "notes",
297
+ bytes_stored=len(content.encode("utf-8")),
298
+ )
299
+ except Exception as exc: # noqa: BLE001
300
+ return KennelRememberOutput(
301
+ drawer_id=0,
302
+ wing=wing,
303
+ room=room,
304
+ bytes_stored=0,
305
+ error=f"kennel_remember failed: {exc!r}",
306
+ )
307
+
308
+
309
+ def register_kennel_recent(agent: Any) -> None:
310
+ """Register the ``kennel_recent`` tool — time-ordered drawer browsing."""
311
+
312
+ @agent.tool
313
+ async def kennel_recent(
314
+ context: RunContext,
315
+ wing: str = "",
316
+ top_k: int = 5,
317
+ scope: str = "default",
318
+ ) -> KennelRecentOutput:
319
+ """List recent drawers without a query. Useful for orienting on a new session.
320
+
321
+ Args:
322
+ wing: Restrict to a single wing (or shortcut: "repo", "agent", "user").
323
+ Leave blank to use ``scope``.
324
+ top_k: Number of drawers (1-50, default 5).
325
+ scope: When ``wing`` is blank: ``"default"`` / ``"repo"`` / ``"agent"``
326
+ / ``"user"`` / ``"all"``. Same semantics as ``kennel_recall``.
327
+
328
+ Returns drawers newest-first, deduplicated by content.
329
+ """
330
+ if not is_enabled():
331
+ return KennelRecentOutput(
332
+ wings_searched=[],
333
+ total=0,
334
+ error=DISABLED_TOOL_ERROR,
335
+ )
336
+ try:
337
+ top_k = max(1, min(int(top_k), 50))
338
+ except (TypeError, ValueError):
339
+ top_k = 5
340
+ try:
341
+ agent_name = _agent_name_from_context(context)
342
+ cwd = detect_cwd()
343
+ wings_to_search = _resolve_scope(wing, scope, agent_name, cwd)
344
+ hits = kennel.recent_drawers_multi(
345
+ wing_names=wings_to_search or None,
346
+ limit=top_k,
347
+ )
348
+ return KennelRecentOutput(
349
+ wings_searched=wings_to_search,
350
+ total=len(hits),
351
+ drawers=[_drawer_to_model(d) for d in hits],
352
+ )
353
+ except Exception as exc: # noqa: BLE001
354
+ return KennelRecentOutput(
355
+ wings_searched=[],
356
+ total=0,
357
+ error=f"kennel_recent failed: {exc!r}",
358
+ )
359
+
360
+
361
+ def register_kennel_list_wings(agent: Any) -> None:
362
+ """Register the ``kennel_list_wings`` tool — wing discovery."""
363
+
364
+ @agent.tool
365
+ async def kennel_list_wings(context: RunContext) -> KennelWingsOutput:
366
+ """List every wing in the kennel with its drawer count.
367
+
368
+ Use this to discover what memory partitions exist before scoping a
369
+ ``kennel_recall`` or ``kennel_recent`` call.
370
+ """
371
+ if not is_enabled():
372
+ return KennelWingsOutput(wings=[], total_wings=0, error=DISABLED_TOOL_ERROR)
373
+ try:
374
+ pairs = kennel.wings_with_counts()
375
+ return KennelWingsOutput(
376
+ wings=[WingInfo(name=n, drawer_count=c) for n, c in pairs],
377
+ total_wings=len(pairs),
378
+ )
379
+ except Exception as exc: # noqa: BLE001
380
+ return KennelWingsOutput(
381
+ wings=[],
382
+ total_wings=0,
383
+ error=f"kennel_list_wings failed: {exc!r}",
384
+ )
385
+
386
+
387
+ def register_kennel_stats(agent: Any) -> None:
388
+ """Register the ``kennel_stats`` tool — kennel-wide totals + size."""
389
+
390
+ @agent.tool
391
+ async def kennel_stats(context: RunContext) -> KennelStatsOutput:
392
+ """Return kennel-wide totals: drawers, wings, and on-disk size.
393
+
394
+ Useful for gating behavior — e.g. skip recall on an empty kennel.
395
+ """
396
+ if not is_enabled():
397
+ return KennelStatsOutput(
398
+ db_path=str(DB_PATH),
399
+ db_size_bytes=0,
400
+ total_drawers=0,
401
+ total_wings=0,
402
+ error=DISABLED_TOOL_ERROR,
403
+ )
404
+ try:
405
+ size = DB_PATH.stat().st_size if DB_PATH.exists() else 0
406
+ return KennelStatsOutput(
407
+ db_path=str(DB_PATH),
408
+ db_size_bytes=size,
409
+ total_drawers=kennel.count_drawers(),
410
+ total_wings=len(kennel.list_wings()),
411
+ )
412
+ except Exception as exc: # noqa: BLE001
413
+ return KennelStatsOutput(
414
+ db_path=str(DB_PATH),
415
+ db_size_bytes=0,
416
+ total_drawers=0,
417
+ total_wings=0,
418
+ error=f"kennel_stats failed: {exc!r}",
419
+ )
420
+
421
+
422
+ def _agent_name_from_context(context: RunContext) -> str:
423
+ """Best-effort extraction of the calling agent's name from the run context.
424
+
425
+ Falls back to ``"unknown"`` if the framework doesn't expose it on this
426
+ version of pydantic_ai.
427
+ """
428
+ for attr in ("agent_name", "name"):
429
+ val = getattr(context, attr, None)
430
+ if val:
431
+ return str(val)
432
+ deps = getattr(context, "deps", None)
433
+ if deps is not None:
434
+ for attr in ("agent_name", "name"):
435
+ val = getattr(deps, attr, None)
436
+ if val:
437
+ return str(val)
438
+ return "unknown"
439
+
440
+
441
+ def register_tools_callback() -> list[dict[str, Any]]:
442
+ """``register_tools`` callback — exposes the full kennel tool surface."""
443
+ return [
444
+ {"name": "kennel_recall", "register_func": register_kennel_recall},
445
+ {"name": "kennel_remember", "register_func": register_kennel_remember},
446
+ {"name": "kennel_recent", "register_func": register_kennel_recent},
447
+ {"name": "kennel_list_wings", "register_func": register_kennel_list_wings},
448
+ {"name": "kennel_stats", "register_func": register_kennel_stats},
449
+ ]
@@ -0,0 +1,110 @@
1
+ """Wing namespacing — the convention that compartmentalizes memory.
2
+
3
+ Three namespaces, all stored in the same shared kennel:
4
+
5
+ * ``repo:<path>`` — project memory, shared between agents in that repo
6
+ * ``agent:<name>`` — per-agent diary, private by convention
7
+ * ``user:default`` — cross-cutting user preferences
8
+
9
+ Keep this module dependency-free; both recorder and retriever import it.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ from pathlib import Path
16
+
17
+ USER_WING = "user:default"
18
+
19
+
20
+ def _resolve_worktree_root(git_path: Path) -> Path | None:
21
+ """Resolve main repo root from a worktree ``.git`` file.
22
+
23
+ Worktrees store ``.git`` as a file with a ``gitdir: ...`` pointer that
24
+ targets ``<main-repo>/.git/worktrees/<name>`` (absolute or relative).
25
+ This helper parses and resolves that pointer, then walks upward until it
26
+ finds the real ``.git`` directory and returns its parent repo root.
27
+ """
28
+ try:
29
+ content = git_path.read_text(encoding="utf-8", errors="replace")
30
+ except (OSError, ValueError):
31
+ return None
32
+
33
+ gitdir_raw: str | None = None
34
+ for raw_line in content.splitlines():
35
+ line = raw_line.strip()
36
+ if line.lower().startswith("gitdir:"):
37
+ gitdir_raw = line[len("gitdir:") :].strip()
38
+ break
39
+
40
+ if not gitdir_raw:
41
+ return None
42
+
43
+ try:
44
+ gitdir_path = Path(gitdir_raw)
45
+ if not gitdir_path.is_absolute():
46
+ gitdir_path = git_path.parent / gitdir_path
47
+ gitdir_path = gitdir_path.resolve()
48
+ except (OSError, ValueError):
49
+ return None
50
+
51
+ # Resolve worktrees (``.git/worktrees``), not submodules (``.git/modules``);
52
+ # submodules are separate repos and keep their own wing.
53
+ if "worktrees" not in gitdir_path.parts:
54
+ return None
55
+
56
+ for parent in (gitdir_path, *gitdir_path.parents):
57
+ if parent.name == ".git" and parent.is_dir():
58
+ return parent.parent
59
+
60
+ return None
61
+
62
+
63
+ def repo_wing(start: Path | str | None = None) -> str:
64
+ """Return the ``repo:<path>`` wing for the given (or current) directory.
65
+
66
+ Walks upward looking for ``.git``. For normal repos, ``.git`` is a
67
+ directory and that candidate directory is used. For git worktrees,
68
+ ``.git`` is a file; in that case this resolves the linked main repo root
69
+ and uses it so all worktrees share one repo wing. If none is found, falls
70
+ back to the resolved start directory itself — bare directories are still
71
+ legitimate project scopes.
72
+ """
73
+ here = Path(start) if start else Path.cwd()
74
+ here = here.resolve()
75
+ for candidate in (here, *here.parents):
76
+ git_path = candidate / ".git"
77
+ if not git_path.exists():
78
+ continue
79
+
80
+ if git_path.is_file():
81
+ worktree_root = _resolve_worktree_root(git_path)
82
+ if worktree_root is not None:
83
+ return f"repo:{worktree_root}"
84
+
85
+ return f"repo:{candidate}"
86
+
87
+ return f"repo:{here}"
88
+
89
+
90
+ def agent_wing(agent_name: str) -> str:
91
+ """Return the ``agent:<name>`` wing for a given agent."""
92
+ safe = (agent_name or "unknown").strip() or "unknown"
93
+ return f"agent:{safe}"
94
+
95
+
96
+ def default_recall_scope(agent_name: str, cwd: Path | str | None = None) -> list[str]:
97
+ """The standard recall set: this repo + this agent + cross-user prefs."""
98
+ return [repo_wing(cwd), agent_wing(agent_name), USER_WING]
99
+
100
+
101
+ def detect_cwd() -> Path:
102
+ """Best-effort current working directory.
103
+
104
+ Falls back to ``$HOME`` if cwd is somehow unavailable (rare but possible
105
+ in some test environments).
106
+ """
107
+ try:
108
+ return Path(os.getcwd())
109
+ except OSError:
110
+ return Path.home()
@@ -0,0 +1,93 @@
1
+ # puppy_spinner
2
+
3
+ The bouncing-puppy spinner on the bottom bar's status-prefix slot —
4
+ now with customizable styles.
5
+
6
+ ## `/spinner` command
7
+
8
+ | Command | What it does |
9
+ |---|---|
10
+ | `/spinner` | Interactive picker with a **live animated preview** |
11
+ | `/spinner <name> [seconds]` | Apply a spinner by name, optionally at a custom speed |
12
+ | `/spinner init` | Write a starter `spinners.json` for custom spinners |
13
+
14
+ Edits to `spinners.json` are picked up automatically (the file's mtime
15
+ is watched) — the currently animating spinner refreshes on its own,
16
+ and the picker reloads its list live *even while it's open*. No reload
17
+ step.
18
+
19
+ Picker navigation matches the `/plugins` menu: `j`/`k` + up/down move the
20
+ selection, `PgUp`/`PgDn` page through the list, `g`/`G` (or `Home`/`End`)
21
+ jump to first/last, `Enter` applies, `q`/`Esc`/`Ctrl-C` cancels.
22
+ `-`/`+` (or `←`/`→`) adjust the highlighted spinner's speed live in the
23
+ preview; `Enter` saves the speed you dialed straight into
24
+ `spinners.json` as that spinner's speed — same place you'd edit by
25
+ hand, and it sticks until you change it again. `i` writes the starter
26
+ `spinners.json` right from the menu (same as `/spinner init`).
27
+
28
+ The choice persists in `puppy.cfg` (`spinner_style` key) and takes
29
+ effect on the very next animation frame — no restart, even mid-run.
30
+
31
+ ## Builtin spinners
32
+
33
+ Every builtin runs at 0.2s per frame by default — per-spinner tuning is
34
+ what the picker's speed keys and `spinners.json` tweaks are for. The
35
+ one exception: the classic `puppy` trots at 0.06s out of the box.
36
+
37
+ The puppy pack: `puppy` (default), `bone`, `zoomies`, `paws`, `dots`.
38
+
39
+ Plus a cli-spinners-style pack: `aesthetic`, `binary`, `bouncingBall`,
40
+ `bouncingBar`, `chevrons`, `dots8Bit`, `dotsCircle`, `dotsWide`,
41
+ `fistBump`, `growHorizontal`, `growVertical`, `noise`, `pong`, `sand`.
42
+
43
+ The picker shows everything alphabetically.
44
+
45
+ Frames for that pack live in `builtin_frames.py` — a generated,
46
+ data-only module (non-ASCII is escape-spelled for the repo emoji
47
+ filter); edit descriptions there, not frame art.
48
+
49
+ ## Custom spinners
50
+
51
+ Drop a `spinners.json` next to `puppy.cfg` (usually
52
+ `~/.code_puppy/spinners.json` — the picker's preview pane shows the
53
+ exact path):
54
+
55
+ ```json
56
+ {
57
+ "sniffer": {
58
+ "frames": ["( . ) ", "( . ) ", "( . ) ", "( . ) "],
59
+ "interval": 0.1,
60
+ "description": "a very minimalist puppy"
61
+ }
62
+ }
63
+ ```
64
+
65
+ ### Re-speeding a builtin
66
+
67
+ An entry that names a builtin and **omits `frames`** is a *tweak*: it
68
+ inherits the builtin's frames and just overrides `interval` and/or
69
+ `description` — no copying frame art:
70
+
71
+ ```json
72
+ {
73
+ "zoomies": { "interval": 0.1 }
74
+ }
75
+ ```
76
+
77
+ Speed has one rule: a spinner runs at its `spinners.json` value when
78
+ present, else the builtin default (0.2s). The picker's speed keys and
79
+ `/spinner <name> <seconds>` don't add another layer — they just save
80
+ that value into the file for you.
81
+
82
+ Rules:
83
+
84
+ - `frames`: non-empty list of non-empty strings (padded to equal width
85
+ automatically; capped at 40 chars per frame). Omit it only when the
86
+ entry name matches a builtin (that's a tweak, see above).
87
+ - `interval`: seconds per frame, clamped to `0.02`–`1.0` (default `0.2`).
88
+ - `description`: optional, shown in the picker's preview pane.
89
+ - **User entries override builtins** on name collision — yes, you can
90
+ redefine `puppy`. We're not mad, just disappointed.
91
+ - A broken file never crashes anything: bad entries are skipped with a
92
+ log warning, and an unknown configured name falls back to the classic
93
+ kennel bounce.
@@ -0,0 +1,11 @@
1
+ """Puppy spinner plugin.
2
+
3
+ Brings back the beloved bouncing-puppy spinner from the pre-bottom-bar
4
+ era: while any agent run is in flight, the dog-face emoji bounces inside
5
+ its parenthesis kennel on the bottom bar's status-prefix slot, right
6
+ next to the token-context info.
7
+
8
+ Not a puppy person? ``/spinner`` opens a live-preview picker with a
9
+ kennel-ful of builtin styles, and ``spinners.json`` (next to
10
+ ``puppy.cfg``) lets you define your own -- see ``/spinner init``.
11
+ """