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,569 @@
1
+ """Register the Wiggum looping slash commands and goal continuation policy."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from typing import Any
7
+
8
+ from code_puppy.callbacks import register_callback
9
+ from code_puppy.command_line.command_registry import register_command
10
+ from code_puppy.config import get_value
11
+ from code_puppy.messaging import (
12
+ emit_info,
13
+ emit_success,
14
+ emit_system_message,
15
+ emit_warning,
16
+ )
17
+
18
+ from . import goal_runs, state
19
+ from .judge import GoalJudgement, judge_goal
20
+ from .judge_config import JudgeConfig, get_enabled_judges_or_default, load_judges
21
+
22
+ # /goal iterations default to 10; /set goal_max_iterations overrides, clamped to
23
+ # [1, 1000].
24
+ GOAL_MAX_ITERATIONS_DEFAULT = 10
25
+ GOAL_MAX_ITERATIONS_FLOOR = 1
26
+ GOAL_MAX_ITERATIONS_CEILING = 1000
27
+
28
+
29
+ def _get_goal_max_iterations() -> int:
30
+ """Read the configured /goal iteration cap, with sane fallbacks."""
31
+ val = get_value("goal_max_iterations")
32
+ try:
33
+ n = int(val) if val else GOAL_MAX_ITERATIONS_DEFAULT
34
+ except (ValueError, TypeError):
35
+ n = GOAL_MAX_ITERATIONS_DEFAULT
36
+ return max(GOAL_MAX_ITERATIONS_FLOOR, min(n, GOAL_MAX_ITERATIONS_CEILING))
37
+
38
+
39
+ # Slash commands
40
+
41
+
42
+ @register_command(
43
+ name="wiggum",
44
+ description="Loop mode: re-run the same prompt when agent finishes 🍩",
45
+ usage="/wiggum <prompt>",
46
+ category="plugin",
47
+ )
48
+ def handle_wiggum_command(command: str) -> str | bool:
49
+ """Start Wiggum loop mode and execute the prompt immediately."""
50
+ prompt = _extract_prompt(command)
51
+ if not prompt:
52
+ emit_warning("Usage: /wiggum <prompt>")
53
+ emit_info("Example: /wiggum say hello world")
54
+ emit_info("Press Ctrl+C or run /wiggum_stop to stop the loop.")
55
+ return True
56
+
57
+ state.start(prompt, mode="wiggum")
58
+ emit_success("🍩 WIGGUM MODE ACTIVATED!")
59
+ emit_info(f"Prompt: {prompt}")
60
+ emit_info("The agent will re-loop this prompt after each completion.")
61
+ emit_info("Press Ctrl+C or run /wiggum_stop to stop the loop.")
62
+ return prompt
63
+
64
+
65
+ @register_command(
66
+ name="goal",
67
+ description="Retry a task until all LLM judges say it is complete 🎯",
68
+ usage="/goal <prompt> | /goal resume <UUID>",
69
+ # /kibble and /chow are puppy-themed aliases for /goal — same behavior,
70
+ # just more on-brand for a code puppy.
71
+ aliases=["kibble", "chow"],
72
+ category="plugin",
73
+ )
74
+ def handle_goal_command(command: str) -> str | bool:
75
+ """Start goal mode and execute the prompt immediately."""
76
+ prompt = _extract_prompt(command)
77
+ if not prompt:
78
+ emit_warning(
79
+ "Usage: /goal <prompt> | /goal resume <UUID> (aliases: /kibble, /chow)"
80
+ )
81
+ emit_info("Example: /goal make tests pass for the auth flow")
82
+ emit_info("Press Ctrl+C or run /goal_stop to stop the loop.")
83
+ return True
84
+
85
+ if prompt.lower().startswith("resume"):
86
+ return _resume_goal(prompt)
87
+
88
+ run = goal_runs.create(prompt)
89
+ state.start(prompt, mode="goal", run_id=run.run_id)
90
+ _display_banner_message(
91
+ "GOAL MODE", "\U0001f3af ACTIVATED!", banner_name="llm_judge"
92
+ )
93
+ emit_info(f"Goal: {prompt}")
94
+ emit_info(f"Goal UUID: {run.run_id}")
95
+ emit_info(
96
+ "After each iteration, every enabled LLM judge will verify completion in parallel."
97
+ )
98
+ emit_info(
99
+ f"Max iterations: {_get_goal_max_iterations()} "
100
+ f"(change with /set goal_max_iterations=<int>)"
101
+ )
102
+ _emit_configured_judges_summary()
103
+ return prompt
104
+
105
+
106
+ @register_command(
107
+ name="wiggum_stop",
108
+ description="Stop Wiggum/goal loop mode",
109
+ usage="/wiggum_stop",
110
+ aliases=["stopwiggum", "ws", "goal_stop"],
111
+ category="plugin",
112
+ )
113
+ def handle_wiggum_stop_command(command: str) -> bool:
114
+ """Stop Wiggum/goal loop mode."""
115
+ del command
116
+ if state.is_active():
117
+ run_id = state.get_state().run_id
118
+ _persist_goal_progress(status="interrupted")
119
+ state.stop()
120
+ emit_success("\U0001f369 Wiggum/goal mode stopped!")
121
+ if run_id:
122
+ emit_info(f"Resume it with: /goal resume {run_id}")
123
+ else:
124
+ emit_info("Wiggum/goal mode is not active.")
125
+ return True
126
+
127
+
128
+ @register_command(
129
+ name="judges",
130
+ description="Configure goal-mode LLM judges (TUI) ⚖️",
131
+ usage="/judges",
132
+ category="plugin",
133
+ )
134
+ def handle_judges_command(command: str) -> bool:
135
+ """Open the goal-judges TUI."""
136
+ del command
137
+ import concurrent.futures
138
+
139
+ from .judges_menu import interactive_judges_menu
140
+
141
+ # The menu is async; run it in a fresh event loop on a worker thread so
142
+ # we don't collide with whatever loop the CLI is using.
143
+ try:
144
+ with concurrent.futures.ThreadPoolExecutor() as executor:
145
+ future = executor.submit(lambda: asyncio.run(interactive_judges_menu()))
146
+ future.result(timeout=600)
147
+ except concurrent.futures.TimeoutError:
148
+ emit_warning("Judges menu timed out.")
149
+ except Exception as exc:
150
+ emit_warning(f"Judges menu error: {exc}")
151
+
152
+ return True
153
+
154
+
155
+ # Helpers
156
+
157
+
158
+ def _emit_configured_judges_summary() -> None:
159
+ """Show the user exactly which judges /goal will fan out to.
160
+
161
+ Previously we just told users to run /judges, which left people
162
+ wondering if they'd configured anything at all. Now we list the
163
+ enabled judges (and warn about disabled ones) so the state of the
164
+ world is obvious before the loop kicks off.
165
+ """
166
+ registry = load_judges()
167
+ enabled = registry.enabled()
168
+ disabled = [j for j in registry.judges if not j.enabled]
169
+
170
+ if enabled:
171
+ emit_info(f"Configured judges ({len(enabled)} enabled):")
172
+ for judge in enabled:
173
+ emit_info(f" • {judge.name} ({judge.model})")
174
+ if disabled:
175
+ disabled_names = ", ".join(j.name for j in disabled)
176
+ emit_info(f" (disabled: {disabled_names})")
177
+ else:
178
+ emit_info(
179
+ "No judges configured — falling back to a single default judge "
180
+ "using the implementor's model."
181
+ )
182
+ emit_info("Run /judges to add, edit, enable, or disable judges.")
183
+
184
+
185
+ def _resume_goal(arguments: str) -> str | bool:
186
+ """Restore a durable goal run and dispatch its next iteration."""
187
+ parts = arguments.split()
188
+ if len(parts) != 2 or parts[0].lower() != "resume":
189
+ emit_warning("Usage: /goal resume <UUID>")
190
+ return True
191
+
192
+ run = goal_runs.load(parts[1])
193
+ if run is None:
194
+ emit_warning(f"Goal run not found: {parts[1]}")
195
+ return True
196
+ if run.status in {"completed", "stopped"}:
197
+ emit_warning(f"Goal run {run.run_id} is already {run.status}.")
198
+ return True
199
+
200
+ state.start(
201
+ run.prompt,
202
+ mode="goal",
203
+ run_id=run.run_id,
204
+ loop_count=run.loop_count,
205
+ remediation_notes=run.remediation_notes,
206
+ )
207
+ goal_runs.update(
208
+ run.run_id,
209
+ loop_count=run.loop_count,
210
+ remediation_notes=run.remediation_notes,
211
+ )
212
+ _display_banner_message("GOAL MODE", "\U0001f3af RESUMED!", banner_name="llm_judge")
213
+ emit_info(f"Goal UUID: {run.run_id}")
214
+ emit_info(f"Resuming after {run.loop_count} completed iteration(s).")
215
+ if run.remediation_notes:
216
+ return f"{run.prompt}\n\nJudge remediation notes:\n{run.remediation_notes}"
217
+ return run.prompt
218
+
219
+
220
+ def _extract_prompt(command: str) -> str:
221
+ parts = command.split(maxsplit=1)
222
+ if len(parts) < 2:
223
+ return ""
224
+ return parts[1].strip()
225
+
226
+
227
+ def _response_text(result: Any) -> str | None:
228
+ return str(getattr(result, "output", "")) if result is not None else None
229
+
230
+
231
+ def _display_banner_message(
232
+ label: str,
233
+ message: str,
234
+ *,
235
+ banner_name: str,
236
+ details: str | None = None,
237
+ final: bool = False,
238
+ ) -> None:
239
+ """Display an inline banner followed by a message.
240
+
241
+ Plain sequential output — it scrolls inside the bottom bar's region,
242
+ so no terminal-ownership dance is needed.
243
+ """
244
+ from rich.console import Console
245
+ from rich.text import Text
246
+
247
+ from code_puppy.config import get_banner_color
248
+ from code_puppy.tools.display import erase_progress_line
249
+
250
+ console = Console()
251
+
252
+ erase_progress_line(console)
253
+ console.print()
254
+ color = get_banner_color(banner_name)
255
+ banner = Text.from_markup(
256
+ f"[bold white on {color}] {label} [/bold white on {color}] "
257
+ )
258
+ console.print(banner, end="")
259
+ # markup=False so brackets in the message (e.g. "[joe-brown]") aren't
260
+ # eaten by Rich's markup parser. Same for `details`.
261
+ console.print(message, markup=False, highlight=False)
262
+
263
+ if details:
264
+ console.print(details, markup=False, highlight=False)
265
+ if final:
266
+ console.print()
267
+
268
+
269
+ def _display_llm_judge(
270
+ message: str, details: str | None = None, *, final: bool = False
271
+ ) -> None:
272
+ """Display goal-judge output with an inline banner."""
273
+ _display_banner_message(
274
+ "LLM JUDGE",
275
+ message,
276
+ banner_name="llm_judge",
277
+ details=details,
278
+ final=final,
279
+ )
280
+
281
+
282
+ def _resolve_judges(implementor_agent: Any) -> list[JudgeConfig]:
283
+ """Pick the judge set for this /goal iteration.
284
+
285
+ If the user has configured judges via ``/judges`` we use those.
286
+ Otherwise we fall back to a single ``default`` judge that uses the
287
+ implementor agent's model and the standard goal-judge prompt.
288
+ """
289
+ fallback_model = getattr(
290
+ implementor_agent.get_pydantic_agent().model
291
+ if hasattr(implementor_agent, "get_pydantic_agent")
292
+ else None,
293
+ "model_name",
294
+ None,
295
+ )
296
+ if not fallback_model:
297
+ # The agent object exposes its model name via get_model_name() in most
298
+ # places; fall back to that if the pydantic_agent shape differs.
299
+ try:
300
+ fallback_model = implementor_agent.get_model_name()
301
+ except Exception:
302
+ fallback_model = "code-puppy"
303
+
304
+ return get_enabled_judges_or_default(str(fallback_model))
305
+
306
+
307
+ def _format_remediation_block(verdicts: list[GoalJudgement]) -> str:
308
+ """Build the remediation-notes string that feeds the next iteration."""
309
+ lines: list[str] = []
310
+ for v in verdicts:
311
+ if v.abstained:
312
+ status = "⚠️ ABSTAIN"
313
+ else:
314
+ status = "✅ PASS" if v.complete else "❌ FAIL"
315
+ lines.append(f"[{v.judge_name}] {status}")
316
+ if v.notes:
317
+ for note_line in v.notes.strip().splitlines():
318
+ lines.append(f" {note_line}")
319
+ lines.append("")
320
+ return "\n".join(lines).rstrip()
321
+
322
+
323
+ # Judge orchestration (parallel)
324
+
325
+
326
+ async def _run_single_judge(
327
+ judge_config: JudgeConfig,
328
+ *,
329
+ implementor_agent: Any,
330
+ goal: str,
331
+ response: str | None,
332
+ error: BaseException | None,
333
+ history: list[Any],
334
+ ) -> GoalJudgement:
335
+ """Run a single judge. No I/O — callers handle display before/after.
336
+
337
+ We intentionally do NOT print here: ``_run_goal_judges`` runs many of
338
+ these in parallel via ``asyncio.gather``, and concurrent calls into the
339
+ rich Console (which does \\r line-clearing tricks) interleave and
340
+ overwrite each other. Display is serialized at the orchestrator level.
341
+ """
342
+ try:
343
+ return await judge_goal(
344
+ judge_config=judge_config,
345
+ implementor_agent=implementor_agent,
346
+ goal=goal,
347
+ response=response,
348
+ error=error,
349
+ history=history,
350
+ )
351
+ except (asyncio.CancelledError, KeyboardInterrupt):
352
+ raise
353
+ except Exception as exc:
354
+ # judge_goal handles model errors; unexpected plumbing errors still abstain
355
+ # so one broken judge cannot block /goal.
356
+ from code_puppy.error_logging import log_error
357
+
358
+ log_error(exc, context=f"Goal judge failed ({judge_config.name})")
359
+ return GoalJudgement(
360
+ judge_name=judge_config.name,
361
+ complete=False,
362
+ notes=f"judge crashed: {type(exc).__name__}: {exc}",
363
+ raw_response="",
364
+ abstained=True,
365
+ )
366
+
367
+
368
+ def _judge_roster_line(judges: list[JudgeConfig]) -> str:
369
+ """e.g. 'judy (gpt-5.4), joe-brown (claude-sonnet-4.5)'."""
370
+ return ", ".join(f"{j.name} ({j.model})" for j in judges)
371
+
372
+
373
+ async def _run_goal_judges(
374
+ *,
375
+ agent: Any,
376
+ goal: str,
377
+ result: Any,
378
+ error: BaseException | None,
379
+ ) -> tuple[bool, str, list[GoalJudgement]]:
380
+ """Run every enabled judge in parallel.
381
+
382
+ Returns ``(all_complete, formatted_notes, verdicts)``.
383
+
384
+ ``all_complete`` is True when every judge reports ``complete=True``.
385
+ The judge's own ``complete=True`` IS the "no remediation needed"
386
+ signal — any rationale notes alongside it are just for visibility and
387
+ don't block completion.
388
+ """
389
+ judges = _resolve_judges(agent)
390
+ if not judges:
391
+ return False, "No judge agents configured.", []
392
+
393
+ history = list(agent.get_message_history())
394
+ response_text = _response_text(result)
395
+
396
+ # Announce up front so the user knows we're firing N judges in parallel.
397
+ if len(judges) == 1:
398
+ _display_llm_judge(
399
+ f"Asking judge {_judge_roster_line(judges)} if the goal is complete..."
400
+ )
401
+ else:
402
+ _display_llm_judge(
403
+ f"Running {len(judges)} judges in parallel: {_judge_roster_line(judges)}"
404
+ )
405
+
406
+ try:
407
+ verdicts: list[GoalJudgement] = await asyncio.gather(
408
+ *(
409
+ _run_single_judge(
410
+ judge,
411
+ implementor_agent=agent,
412
+ goal=goal,
413
+ response=response_text,
414
+ error=error,
415
+ history=history,
416
+ )
417
+ for judge in judges
418
+ )
419
+ )
420
+ except (asyncio.CancelledError, KeyboardInterrupt):
421
+ # Show why judges bailed, then re-raise so the caller keeps the REPL alive
422
+ # and stops the loop; a sentinel would trigger another retry.
423
+ _display_llm_judge("⛔ Judges cancelled (Ctrl+C). Stopping goal loop.")
424
+ raise
425
+
426
+ # Now serialize the per-judge verdicts so all banners actually show up.
427
+ for v in verdicts:
428
+ if v.abstained:
429
+ glyph = "⚠️ ABSTAIN"
430
+ else:
431
+ glyph = "✅ PASS" if v.complete else "❌ FAIL"
432
+ summary = v.notes.strip().splitlines()[0] if v.notes.strip() else "(no notes)"
433
+ _display_llm_judge(f" [{v.judge_name}] {glyph} — {summary}")
434
+
435
+ # Abstentions do not vote; completion requires every non-abstaining judge to
436
+ # pass. If all abstain, report incomplete because completion is undecidable.
437
+ voting = [v for v in verdicts if not v.abstained]
438
+ if not voting:
439
+ all_complete = False
440
+ if verdicts:
441
+ _display_llm_judge("⚠️ All judges abstained — cannot determine completion.")
442
+ else:
443
+ all_complete = all(v.complete for v in voting)
444
+
445
+ notes = _format_remediation_block(verdicts)
446
+ return all_complete, notes, verdicts
447
+
448
+
449
+ # Turn-end hook (drives /goal and /wiggum loops)
450
+
451
+
452
+ async def _on_interactive_turn_end(
453
+ agent: Any,
454
+ prompt: str,
455
+ result: Any = None,
456
+ *,
457
+ success: bool = True,
458
+ error: BaseException | None = None,
459
+ ) -> dict[str, Any] | None:
460
+ """Ask the CLI to continue while Wiggum/goal mode is active."""
461
+ del prompt, success
462
+ goal_prompt = state.get_prompt()
463
+ if not goal_prompt:
464
+ state.stop()
465
+ return None
466
+
467
+ loop_num = state.increment()
468
+ if state.is_goal_mode():
469
+ try:
470
+ complete, notes, _verdicts = await _run_goal_judges(
471
+ agent=agent,
472
+ goal=goal_prompt,
473
+ result=result,
474
+ error=error,
475
+ )
476
+ except (asyncio.CancelledError, KeyboardInterrupt):
477
+ # Keep enough durable state for an explicit UUID resume.
478
+ run_id = state.get_state().run_id
479
+ _persist_goal_progress(status="interrupted")
480
+ _display_llm_judge("\u26d4 Goal loop cancelled (Ctrl+C).")
481
+ state.stop()
482
+ if run_id:
483
+ emit_info(f"Resume it with: /goal resume {run_id}")
484
+ return None
485
+ if complete:
486
+ _persist_goal_progress(status="completed")
487
+ # Per-judge verdicts were already shown by _run_goal_judges —
488
+ # no need to re-dump the notes block here.
489
+ _display_llm_judge("✅ GOAL COMPLETE!", final=True)
490
+ state.stop()
491
+ return None
492
+
493
+ max_iters = _get_goal_max_iterations()
494
+ if loop_num >= max_iters:
495
+ _persist_goal_progress(status="stopped")
496
+ _display_llm_judge(
497
+ f"🛑 GOAL STOPPED — Hit max iterations ({max_iters}). "
498
+ f"Raise the cap with /set goal_max_iterations=<int>.",
499
+ final=True,
500
+ )
501
+ state.stop()
502
+ return None
503
+
504
+ state.get_state().remediation_notes = notes
505
+ _persist_goal_progress()
506
+ _display_llm_judge(
507
+ f"❌ GOAL INCOMPLETE — Retrying! (Loop #{loop_num}/{max_iters})",
508
+ final=True,
509
+ )
510
+ return {
511
+ "prompt": f"{goal_prompt}\n\nJudge remediation notes:\n{notes}",
512
+ "clear_context": True,
513
+ "delay": 0.5,
514
+ "reason": "goal",
515
+ }
516
+
517
+ if error is not None:
518
+ emit_warning(f"\n🍩 WIGGUM RETRYING AFTER ERROR! (Loop #{loop_num})")
519
+ emit_system_message(f"Previous run failed: {error}")
520
+ else:
521
+ emit_warning(f"\n🍩 WIGGUM RELOOPING! (Loop #{loop_num})")
522
+
523
+ emit_system_message(f"Re-running prompt: {goal_prompt}")
524
+ return {
525
+ "prompt": goal_prompt,
526
+ "clear_context": True,
527
+ "delay": 0.5,
528
+ "reason": "wiggum",
529
+ }
530
+
531
+
532
+ def _persist_goal_progress(*, status: str = "active") -> None:
533
+ current = state.get_state()
534
+ if current.mode != "goal" or current.run_id is None:
535
+ return
536
+ goal_runs.update(
537
+ current.run_id,
538
+ loop_count=current.loop_count,
539
+ remediation_notes=current.remediation_notes,
540
+ status=status,
541
+ )
542
+
543
+
544
+ def _on_interactive_turn_cancel(prompt: str, *, reason: str = "cancelled") -> None:
545
+ del prompt
546
+ if state.is_active():
547
+ current = state.get_state()
548
+ run_id = current.run_id
549
+ _persist_goal_progress(status="interrupted")
550
+ state.stop()
551
+ emit_warning(f"\U0001f369 Wiggum/goal loop stopped due to {reason}")
552
+ if run_id:
553
+ emit_info(f"Resume it with: /goal resume {run_id}")
554
+
555
+
556
+ def _autonomous_loop_capability(name: str) -> bool | None:
557
+ """Advertise the autonomous-loop state through ``feature_capability``.
558
+
559
+ Core (e.g. ask_user_question) checks ``autonomous_loop`` to decide
560
+ whether interactive tools should be blocked, without importing wiggum.
561
+ """
562
+ if name != "autonomous_loop":
563
+ return None
564
+ return state.is_active()
565
+
566
+
567
+ register_callback("interactive_turn_end", _on_interactive_turn_end)
568
+ register_callback("interactive_turn_cancel", _on_interactive_turn_cancel)
569
+ register_callback("feature_capability", _autonomous_loop_capability)
@@ -0,0 +1,89 @@
1
+ """State for the Wiggum/goal continuation plugin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass
9
+ class WiggumState:
10
+ """Tiny state container. No behavior soup, please and thank you."""
11
+
12
+ active: bool = False
13
+ prompt: str | None = None
14
+ loop_count: int = 0
15
+ mode: str = "wiggum"
16
+ remediation_notes: str | None = None
17
+ run_id: str | None = None
18
+
19
+ def start(
20
+ self,
21
+ prompt: str,
22
+ *,
23
+ mode: str = "wiggum",
24
+ run_id: str | None = None,
25
+ loop_count: int = 0,
26
+ remediation_notes: str | None = None,
27
+ ) -> None:
28
+ self.active = True
29
+ self.prompt = prompt
30
+ self.loop_count = loop_count
31
+ self.mode = mode
32
+ self.remediation_notes = remediation_notes
33
+ self.run_id = run_id
34
+
35
+ def stop(self) -> None:
36
+ self.active = False
37
+ self.prompt = None
38
+ self.loop_count = 0
39
+ self.mode = "wiggum"
40
+ self.remediation_notes = None
41
+ self.run_id = None
42
+
43
+ def increment(self) -> int:
44
+ self.loop_count += 1
45
+ return self.loop_count
46
+
47
+
48
+ _STATE = WiggumState()
49
+
50
+
51
+ def get_state() -> WiggumState:
52
+ return _STATE
53
+
54
+
55
+ def is_active() -> bool:
56
+ return _STATE.active
57
+
58
+
59
+ def is_goal_mode() -> bool:
60
+ return _STATE.active and _STATE.mode == "goal"
61
+
62
+
63
+ def get_prompt() -> str | None:
64
+ return _STATE.prompt if _STATE.active else None
65
+
66
+
67
+ def start(
68
+ prompt: str,
69
+ *,
70
+ mode: str = "wiggum",
71
+ run_id: str | None = None,
72
+ loop_count: int = 0,
73
+ remediation_notes: str | None = None,
74
+ ) -> None:
75
+ _STATE.start(
76
+ prompt,
77
+ mode=mode,
78
+ run_id=run_id,
79
+ loop_count=loop_count,
80
+ remediation_notes=remediation_notes,
81
+ )
82
+
83
+
84
+ def stop() -> None:
85
+ _STATE.stop()
86
+
87
+
88
+ def increment() -> int:
89
+ return _STATE.increment()
@@ -0,0 +1 @@
1
+ """Runtime-only CLI and session controls for YOLO mode."""
@@ -0,0 +1,41 @@
1
+ """Expose a runtime-only YOLO override through the CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from typing import Any, Optional
7
+
8
+ from code_puppy.callbacks import register_callback
9
+ from code_puppy.config import set_cli_yolo_override
10
+
11
+
12
+ def _parse_bool(value: str) -> bool:
13
+ """Parse the explicit boolean syntax used by ``--yolo``."""
14
+ normalized = value.strip().lower()
15
+ if normalized == "true":
16
+ return True
17
+ if normalized == "false":
18
+ return False
19
+ raise argparse.ArgumentTypeError("expected 'true' or 'false'")
20
+
21
+
22
+ def _register_cli_args(parser: Any) -> None:
23
+ parser.add_argument(
24
+ "--yolo",
25
+ type=_parse_bool,
26
+ default=None,
27
+ metavar="{true,false}",
28
+ help=(
29
+ "Override YOLO mode for this run without changing puppy.cfg "
30
+ "(/set yolo_mode takes precedence)"
31
+ ),
32
+ )
33
+
34
+
35
+ def _handle_cli_args(args: Any) -> Optional[dict]:
36
+ set_cli_yolo_override(getattr(args, "yolo", None))
37
+ return None
38
+
39
+
40
+ register_callback("register_cli_args", _register_cli_args)
41
+ register_callback("handle_cli_args", _handle_cli_args)