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,77 @@
1
+ """Quick-resume workspace observation hooks.
2
+
3
+ The autosave layer records a quick-resume pointer for cwd. This plugin teaches
4
+ Code Puppy about *child* workspaces touched through tools, so a later
5
+ ``code-puppy -qr ./child`` (or ``/quick-resume ./child``) resolves the session
6
+ even when the process was launched from the parent directory the whole time.
7
+
8
+ Observation is a pure convenience: it only registers hashed pointer keys and
9
+ never blocks or alters tool output.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+
16
+ from code_puppy.callbacks import register_callback
17
+
18
+ # Map tools to plain local-path args; ``path_kind`` tells the resolver whether
19
+ # to probe the file's parent. Nested/non-string payloads are intentionally omitted.
20
+ _LOCAL_PATH_TOOL_ARGS: dict[str, tuple[tuple[str, str], ...]] = {
21
+ "agent_run_shell_command": (("cwd", "directory"),),
22
+ "create_file": (("file_path", "file"),),
23
+ "delete_file": (("file_path", "file"),),
24
+ "delete_snippet": (("file_path", "file"),),
25
+ "grep": (("directory", "directory"),),
26
+ "list_files": (("directory", "directory"),),
27
+ "read_file": (("file_path", "file"),),
28
+ "replace_in_file": (("file_path", "file"),),
29
+ }
30
+
31
+
32
+ def _result_failed(result: Any) -> bool:
33
+ """Return True when a tool result clearly represents a failure.
34
+
35
+ Handles both dict results and objects exposing ``error``/``success``.
36
+ """
37
+ if isinstance(result, dict):
38
+ if result.get("error"):
39
+ return True
40
+ if result.get("success") is False:
41
+ return True
42
+ error = getattr(result, "error", None)
43
+ if error:
44
+ return True
45
+ success = getattr(result, "success", None)
46
+ return success is False
47
+
48
+
49
+ def _observe_tool_paths(
50
+ tool_name: str,
51
+ tool_args: dict[str, Any],
52
+ result: Any,
53
+ duration_ms: float,
54
+ context: Any = None,
55
+ ) -> None:
56
+ """Record local workspace paths from successful tool calls (best-effort)."""
57
+ _ = duration_ms, context # unused; kept for the post_tool_call signature
58
+ if _result_failed(result):
59
+ return
60
+
61
+ path_specs = _LOCAL_PATH_TOOL_ARGS.get(tool_name)
62
+ if not path_specs:
63
+ return
64
+
65
+ try:
66
+ from code_puppy.config import observe_quick_resume_path
67
+
68
+ for arg_name, path_kind in path_specs:
69
+ raw_path = tool_args.get(arg_name)
70
+ if isinstance(raw_path, str) and raw_path.strip():
71
+ observe_quick_resume_path(raw_path, path_kind=path_kind)
72
+ except Exception:
73
+ # Observation is a convenience pointer, never worth breaking tool output.
74
+ return
75
+
76
+
77
+ register_callback("post_tool_call", _observe_tool_paths)
@@ -0,0 +1 @@
1
+ """/review-pr custom slash command plugin."""
@@ -0,0 +1,162 @@
1
+ """Plugin: `/review-pr` — hand the agent a structured PR review mission.
2
+
3
+ Usage:
4
+ /review-pr Review the PR for the current branch
5
+ /review-pr 123 Review PR #123 in the current repo
6
+ /review-pr <github-url> Review the PR at the given URL
7
+ /review-pr --help Show usage
8
+
9
+ The command itself is dumb on purpose (YAGNI): it just builds a beefy prompt
10
+ and returns it. The *agent* does the heavy lifting via `gh` + shell tools.
11
+ That keeps responsibilities clean (SRP) — plugin = command framing, agent =
12
+ code review.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import List, Optional, Tuple
18
+
19
+ from code_puppy.callbacks import register_callback
20
+ from code_puppy.messaging import emit_error, emit_info
21
+
22
+
23
+ COMMAND_NAME = "review-pr"
24
+
25
+
26
+ _USAGE = (
27
+ "Usage: /review-pr [PR_NUMBER | GITHUB_PR_URL]\n"
28
+ " /review-pr → review the current branch's open PR\n"
29
+ " /review-pr 123 → review PR #123 in the current repo\n"
30
+ " /review-pr <url> → review the PR at that GitHub URL"
31
+ )
32
+
33
+
34
+ def _custom_help() -> List[Tuple[str, str]]:
35
+ return [
36
+ (
37
+ COMMAND_NAME,
38
+ "Review a GitHub PR (uses `gh`). Optional arg: PR number or URL.",
39
+ )
40
+ ]
41
+
42
+
43
+ def _parse_target(command: str) -> Optional[str]:
44
+ """Return the raw PR target (number, URL, or '' for current branch).
45
+
46
+ Returns None if the user asked for --help (already handled).
47
+ """
48
+ parts = command.split(maxsplit=1)
49
+ if len(parts) == 1:
50
+ return ""
51
+
52
+ arg = parts[1].strip()
53
+ if arg in {"--help", "-h", "help"}:
54
+ emit_info(_USAGE)
55
+ return None
56
+ return arg
57
+
58
+
59
+ def _build_prompt(target: str) -> str:
60
+ """Compose the review instructions sent to the agent."""
61
+ if target:
62
+ target_clause = (
63
+ f"The user wants you to review this PR: **{target}**.\n"
64
+ "Resolve it with the `gh` CLI (number → current repo, URL → that repo)."
65
+ )
66
+ else:
67
+ target_clause = (
68
+ "No PR was specified — review the PR associated with the **current branch**.\n"
69
+ "Use `gh pr view --json number,url,title,headRefName,baseRefName` to discover it.\n"
70
+ "If no PR exists for the current branch, stop and tell the user."
71
+ )
72
+
73
+ return f"""You are doing a **pull request review**. Be thorough, fair, and a little spicy.
74
+
75
+ {target_clause}
76
+
77
+ ## Step 1 — Gather context
78
+ Run (in order, only what you need):
79
+ 1. `gh pr view <target> --json number,title,author,url,baseRefName,headRefName,body,additions,deletions,changedFiles,labels,isDraft,mergeable,reviewDecision`
80
+ 2. `gh pr diff <target>` to read the actual diff
81
+ 3. `gh pr checks <target>` for CI status
82
+ 4. (Optional) `gh pr view <target> --comments` if you need existing discussion
83
+
84
+ If any command fails (auth, not-a-repo, no PR), surface a clean error and stop.
85
+
86
+ ## Step 2 — Analyze
87
+ Walk the diff and judge it on:
88
+ - **Correctness** — bugs, off-by-ones, race conditions, error handling, edge cases
89
+ - **Design** — SOLID, DRY, YAGNI violations; leaky abstractions; tight coupling
90
+ - **Readability** — naming, function size, comments where the *why* is non-obvious
91
+ - **Tests** — coverage for new logic, missing edge cases, brittle mocks
92
+ - **Security** — injection, secrets, unvalidated input, authz gaps
93
+ - **Performance** — obvious quadratic loops, N+1 queries, needless allocations
94
+ - **Style/Lint** — only call out if it's actually wrong, not just taste
95
+
96
+ Honor the Zen of Python even for non-Python code.
97
+
98
+ ## Step 3 — Report
99
+ Output a single markdown report with these sections (omit empty ones):
100
+
101
+ ### 🐶 PR Review: <title> (#<number>)
102
+ - **Author:** … **Base ← Head:** … **CI:** … **Mergeable:** …
103
+ - **TL;DR:** one-sentence verdict.
104
+
105
+ ### ✅ What's good
106
+ Bullet list. Be specific — point at files/functions.
107
+
108
+ ### 🔴 Blocking issues
109
+ Things that *must* be fixed before merge. Each item:
110
+ - **File:line** — what's wrong, why, and a concrete fix.
111
+
112
+ ### 🟡 Suggestions (non-blocking)
113
+ Nice-to-haves, refactors, follow-ups.
114
+
115
+ ### 🧪 Test gaps
116
+ Specific scenarios that should have tests.
117
+
118
+ ### 🏁 Recommendation
119
+ One of: **APPROVE**, **REQUEST CHANGES**, or **COMMENT** — with a one-line justification.
120
+
121
+ ## Rules
122
+ - Do **NOT** post the review to GitHub. Just print it. The user decides whether to submit.
123
+ - Don't run mutating `gh` commands (no `gh pr review --approve`, no merges, no closes).
124
+ - If the diff is huge (>1500 lines), say so and focus on the highest-signal files.
125
+ - Quote short snippets (≤10 lines) when calling out specific code.
126
+ """
127
+
128
+
129
+ def _handle_custom_command(command: str, name: str) -> Optional[object]:
130
+ if name != COMMAND_NAME:
131
+ return None
132
+
133
+ target = _parse_target(command)
134
+ if target is None:
135
+ # --help was shown; mark as handled without invoking the model.
136
+ return True
137
+
138
+ # Sanity-check gh is available so we fail fast with a friendly hint.
139
+ import shutil
140
+
141
+ if shutil.which("gh") is None:
142
+ emit_error(
143
+ "/review-pr: GitHub CLI (`gh`) not found on PATH. "
144
+ "Install it from https://cli.github.com/ and run `gh auth login`."
145
+ )
146
+ return True
147
+
148
+ emit_info(f"🐶 Fetching PR review mission for: {target or '<current branch>'}")
149
+ return _build_prompt(target)
150
+
151
+
152
+ register_callback("custom_command_help", _custom_help)
153
+ register_callback("custom_command", _handle_custom_command)
154
+
155
+
156
+ __all__ = [
157
+ "COMMAND_NAME",
158
+ "_custom_help",
159
+ "_handle_custom_command",
160
+ "_build_prompt",
161
+ "_parse_target",
162
+ ]
@@ -0,0 +1,6 @@
1
+ """Shell command safety checking plugin.
2
+
3
+ This plugin provides AI-powered safety assessment for shell commands
4
+ executed in yolo_mode. It helps prevent accidental execution of
5
+ dangerous commands that could cause data loss or system damage.
6
+ """
@@ -0,0 +1,69 @@
1
+ """Shell command safety assessment agent.
2
+
3
+ This agent provides rapid risk assessment of shell commands before execution.
4
+ It's designed to be ultra-lightweight with a concise prompt (<200 tokens) and
5
+ uses structured output for reliable parsing.
6
+ """
7
+
8
+ from typing import TYPE_CHECKING, List
9
+
10
+ from code_puppy.agents.base_agent import BaseAgent
11
+
12
+ if TYPE_CHECKING:
13
+ pass
14
+
15
+
16
+ class ShellSafetyAgent(BaseAgent):
17
+ """Lightweight agent for assessing shell command safety risks.
18
+
19
+ This agent evaluates shell commands for potential risks including:
20
+ - File system destruction (rm -rf, dd, format, mkfs)
21
+ - Database operations (DROP, TRUNCATE, unfiltered UPDATE/DELETE)
22
+ - Privilege escalation (sudo, su, chmod 777)
23
+ - Network operations (wget/curl to unknown hosts)
24
+ - Data exfiltration patterns
25
+
26
+ The agent returns structured output with a risk level and brief reasoning.
27
+ """
28
+
29
+ @property
30
+ def name(self) -> str:
31
+ """Agent name for internal use."""
32
+ return "shell_safety_checker"
33
+
34
+ @property
35
+ def display_name(self) -> str:
36
+ """User-facing display name."""
37
+ return "Shell Safety Checker 🛡️"
38
+
39
+ @property
40
+ def description(self) -> str:
41
+ """Agent description."""
42
+ return "Lightweight agent that assesses shell command safety risks"
43
+
44
+ def get_system_prompt(self) -> str:
45
+ """Get the ultra-concise system prompt for shell safety assessment.
46
+
47
+ This prompt is kept under 200 tokens for fast inference and low cost.
48
+ """
49
+ return """You are a shell command safety analyzer. Assess risk levels concisely.
50
+
51
+ **Risk Levels:**
52
+ - none: Completely safe (ls, pwd, echo, cat readonly files)
53
+ - low: Minimal risk (mkdir, touch, git status, read-only queries)
54
+ - medium: Moderate risk (file edits, package installs, service restarts)
55
+ - high: Significant risk (rm files, UPDATE/DELETE without WHERE, TRUNCATE, chmod dangerous permissions)
56
+ - critical: Severe/destructive (rm -rf, DROP TABLE/DATABASE, dd, format, mkfs, bq delete dataset, unfiltered mass deletes)
57
+
58
+ **Evaluate:**
59
+ - Scope (single file vs. entire system)
60
+ - Reversibility (can it be undone?)
61
+ - Data loss potential
62
+ - Privilege requirements
63
+ - Database destruction patterns
64
+
65
+ **Output:** Risk level + reasoning (max 1 sentence)."""
66
+
67
+ def get_available_tools(self) -> List[str]:
68
+ """This agent uses no tools - pure reasoning only."""
69
+ return []
@@ -0,0 +1,156 @@
1
+ """Caching layer for shell command safety assessments.
2
+
3
+ This module provides an LRU cache for recently assessed commands to avoid redundant API calls.
4
+
5
+ The approach is simple and secure: let the LLM assess ALL commands and cache
6
+ those assessments. This eliminates the security risks of pre-defined whitelists
7
+ while providing the performance benefits of caching.
8
+ """
9
+
10
+ from collections import OrderedDict
11
+ from dataclasses import dataclass
12
+ from typing import Optional, Tuple
13
+
14
+ # Maximum number of cached assessments (LRU eviction after this)
15
+ MAX_CACHE_SIZE = 200
16
+
17
+
18
+ @dataclass
19
+ class CachedAssessment:
20
+ """A cached safety assessment result."""
21
+
22
+ risk: str
23
+ reasoning: str
24
+
25
+
26
+ class CommandSafetyCache:
27
+ """LRU cache for shell command safety assessments.
28
+
29
+ This cache stores previous LLM assessments to avoid redundant API calls.
30
+ It uses an OrderedDict for O(1) LRU eviction.
31
+ """
32
+
33
+ def __init__(self, max_size: int = MAX_CACHE_SIZE):
34
+ self._cache: OrderedDict[Tuple[str, Optional[str]], CachedAssessment] = (
35
+ OrderedDict()
36
+ )
37
+ self._max_size = max_size
38
+ self._hits = 0
39
+ self._misses = 0
40
+
41
+ def _make_key(self, command: str, cwd: Optional[str]) -> Tuple[str, Optional[str]]:
42
+ """Create a cache key from command and cwd."""
43
+ # Normalize command (strip whitespace)
44
+ return (command.strip(), cwd)
45
+
46
+ def get(
47
+ self, command: str, cwd: Optional[str] = None
48
+ ) -> Optional[CachedAssessment]:
49
+ """Get a cached assessment if it exists.
50
+
51
+ Args:
52
+ command: The shell command
53
+ cwd: Optional working directory
54
+
55
+ Returns:
56
+ CachedAssessment if found, None otherwise
57
+ """
58
+ key = self._make_key(command, cwd)
59
+ if key in self._cache:
60
+ # Move to end (most recently used)
61
+ self._cache.move_to_end(key)
62
+ self._hits += 1
63
+ return self._cache[key]
64
+ self._misses += 1
65
+ return None
66
+
67
+ def put(
68
+ self, command: str, cwd: Optional[str], assessment: CachedAssessment
69
+ ) -> None:
70
+ """Store an assessment in the cache.
71
+
72
+ Args:
73
+ command: The shell command
74
+ cwd: Optional working directory
75
+ assessment: The assessment result to cache
76
+ """
77
+ key = self._make_key(command, cwd)
78
+
79
+ # If already exists, update and move to end
80
+ if key in self._cache:
81
+ self._cache.move_to_end(key)
82
+ self._cache[key] = assessment
83
+ return
84
+
85
+ # Evict oldest if at capacity
86
+ while len(self._cache) >= self._max_size:
87
+ self._cache.popitem(last=False)
88
+
89
+ self._cache[key] = assessment
90
+
91
+ def clear(self) -> None:
92
+ """Clear all cached assessments."""
93
+ self._cache.clear()
94
+ self._hits = 0
95
+ self._misses = 0
96
+
97
+ @property
98
+ def stats(self) -> dict:
99
+ """Get cache statistics."""
100
+ total = self._hits + self._misses
101
+ hit_rate = (self._hits / total * 100) if total > 0 else 0
102
+ return {
103
+ "size": len(self._cache),
104
+ "max_size": self._max_size,
105
+ "hits": self._hits,
106
+ "misses": self._misses,
107
+ "hit_rate": f"{hit_rate:.1f}%",
108
+ }
109
+
110
+
111
+ # Global cache instance (singleton for the session)
112
+ _cache = CommandSafetyCache()
113
+
114
+
115
+ def get_cache_stats() -> dict:
116
+ """Get statistics about the cache performance."""
117
+ return _cache.stats
118
+
119
+
120
+ def get_cached_assessment(
121
+ command: str, cwd: Optional[str] = None
122
+ ) -> Optional[CachedAssessment]:
123
+ """Get a cached command safety assessment.
124
+
125
+ Cache-only approach: use the LLM cache for speed, but let the LLM
126
+ determine safety for all commands. No pre-defined whitelists.
127
+
128
+ Args:
129
+ command: The shell command to check
130
+ cwd: Optional working directory
131
+
132
+ Returns:
133
+ CachedAssessment if found in cache, None if needs LLM assessment
134
+ """
135
+ return _cache.get(command, cwd)
136
+
137
+
138
+ def cache_assessment(
139
+ command: str, cwd: Optional[str], risk: str, reasoning: str
140
+ ) -> None:
141
+ """Cache an LLM assessment result.
142
+
143
+ Cache all LLM assessments since the same command should get
144
+ the same assessment, providing both security and performance.
145
+
146
+ Args:
147
+ command: The shell command
148
+ cwd: Optional working directory
149
+ risk: The assessed risk level
150
+ reasoning: The assessment reasoning
151
+ """
152
+ assessment = CachedAssessment(
153
+ risk=risk,
154
+ reasoning=reasoning,
155
+ )
156
+ _cache.put(command, cwd, assessment)
@@ -0,0 +1,203 @@
1
+ """Callback registration for shell command safety checking.
2
+
3
+ This module registers a callback that intercepts shell commands in yolo_mode
4
+ and assesses their safety risk before execution.
5
+ """
6
+
7
+ from typing import Any, Dict, Optional
8
+
9
+ from code_puppy.callbacks import register_callback
10
+ from code_puppy.config import (
11
+ get_global_model_name,
12
+ get_safety_permission_level,
13
+ get_yolo_mode,
14
+ )
15
+ from code_puppy.messaging import emit_info
16
+ from code_puppy_core_plugins.shell_safety.command_cache import (
17
+ cache_assessment,
18
+ get_cached_assessment,
19
+ )
20
+ from code_puppy.tools.command_runner import ShellSafetyAssessment
21
+
22
+ # OAuth model prefixes - these models have their own safety mechanisms
23
+ OAUTH_MODEL_PREFIXES = (
24
+ "claude-code-", # Anthropic OAuth
25
+ "codex-", # OpenAI OAuth
26
+ "chatgpt-", # Legacy OpenAI OAuth
27
+ "gemini-oauth", # Google OAuth
28
+ )
29
+
30
+
31
+ def is_oauth_model(model_name: str | None) -> bool:
32
+ """Check if the model is an OAuth model that should skip safety checks.
33
+
34
+ OAuth models have their own built-in safety mechanisms, so we skip
35
+ the shell safety callback to avoid redundant checks and potential bugs.
36
+
37
+ Args:
38
+ model_name: The name of the current model
39
+
40
+ Returns:
41
+ True if the model is an OAuth model, False otherwise
42
+ """
43
+ if not model_name:
44
+ return False
45
+ return model_name.startswith(OAUTH_MODEL_PREFIXES)
46
+
47
+
48
+ # Numeric risk hierarchy: lower values are safer.
49
+ RISK_LEVELS: Dict[str, int] = {
50
+ "none": 0,
51
+ "low": 1,
52
+ "medium": 2,
53
+ "high": 3,
54
+ "critical": 4,
55
+ }
56
+
57
+
58
+ def compare_risk_levels(assessed_risk: Optional[str], threshold: str) -> bool:
59
+ """Compare assessed risk against threshold.
60
+
61
+ Args:
62
+ assessed_risk: The risk level from the agent (can be None)
63
+ threshold: The configured risk threshold
64
+
65
+ Returns:
66
+ True if the command should be blocked (risk exceeds threshold)
67
+ False if the command is acceptable
68
+ """
69
+ # If assessment failed (None), treat as high risk (fail-safe behavior)
70
+ if assessed_risk is None:
71
+ assessed_risk = "high"
72
+
73
+ # Map risk names to numeric values; unknown defaults are conservative.
74
+ assessed_level = RISK_LEVELS.get(assessed_risk, 4) # Default to critical if unknown
75
+ threshold_level = RISK_LEVELS.get(threshold, 2) # Default to medium if unknown
76
+
77
+ # Block if assessed risk is GREATER than threshold
78
+ # Note: Commands AT the threshold level are allowed (>, not >=)
79
+ return assessed_level > threshold_level
80
+
81
+
82
+ async def shell_safety_callback(
83
+ context: Any, command: str, cwd: Optional[str] = None, timeout: int = 60
84
+ ) -> Optional[Dict[str, Any]]:
85
+ """Callback to assess shell command safety before execution.
86
+
87
+ This callback is only active when yolo_mode is True. When yolo_mode is False,
88
+ the user manually reviews every command, so we don't need the agent.
89
+
90
+ Args:
91
+ context: The execution context
92
+ command: The shell command to execute
93
+ cwd: Optional working directory
94
+ timeout: Command timeout (unused here)
95
+
96
+ Returns:
97
+ None if command is safe to proceed
98
+ Dict with rejection info if command should be blocked
99
+ """
100
+ # Skip safety checks for OAuth models - they have their own safety mechanisms
101
+ current_model = get_global_model_name()
102
+ if is_oauth_model(current_model):
103
+ return None
104
+
105
+ # Only check safety in yolo_mode - otherwise user is reviewing manually
106
+ yolo_mode = get_yolo_mode()
107
+ if not yolo_mode:
108
+ return None
109
+
110
+ # Get configured risk threshold
111
+ threshold = get_safety_permission_level()
112
+
113
+ try:
114
+ # Check cache first (fast path - no LLM call)
115
+ cached = get_cached_assessment(command, cwd)
116
+
117
+ if cached:
118
+ # Got a cached result - check against threshold
119
+ if compare_risk_levels(cached.risk, threshold):
120
+ # Cached result says it's too risky
121
+ risk_display = cached.risk or "unknown"
122
+ concise_reason = cached.reasoning or "No reasoning provided"
123
+ error_msg = (
124
+ f"🛑 Command blocked (risk {risk_display.upper()} > permission {threshold.upper()}).\n"
125
+ f"Reason: {concise_reason}\n"
126
+ f"Override: /set yolo_mode true or /set safety_permission_level {risk_display}"
127
+ )
128
+ emit_info(error_msg)
129
+ return {
130
+ "blocked": True,
131
+ "risk": cached.risk,
132
+ "reasoning": cached.reasoning,
133
+ "error_message": error_msg,
134
+ }
135
+ # Cached result is within threshold - allow silently
136
+ return None
137
+
138
+ # Cache miss - need LLM assessment
139
+ # Import here to avoid circular imports
140
+ from code_puppy_core_plugins.shell_safety.agent_shell_safety import (
141
+ ShellSafetyAgent,
142
+ )
143
+
144
+ # Create agent and assess command
145
+ agent = ShellSafetyAgent()
146
+
147
+ # Build the assessment prompt with optional cwd context
148
+ prompt = f"Assess this shell command:\n\nCommand: {command}"
149
+ if cwd:
150
+ prompt += f"\nWorking directory: {cwd}"
151
+
152
+ # Run async assessment with structured output type
153
+ result = await agent.run_with_mcp(prompt, output_type=ShellSafetyAssessment)
154
+ assessment = result.output
155
+
156
+ # Cache the result for future use, but only if it's not a fallback assessment
157
+ if not getattr(assessment, "is_fallback", False):
158
+ cache_assessment(command, cwd, assessment.risk, assessment.reasoning)
159
+
160
+ # Check if risk exceeds threshold (commands at threshold are allowed)
161
+ if compare_risk_levels(assessment.risk, threshold):
162
+ risk_display = assessment.risk or "unknown"
163
+ concise_reason = assessment.reasoning or "No reasoning provided"
164
+ error_msg = (
165
+ f"🛑 Command blocked (risk {risk_display.upper()} > permission {threshold.upper()}).\n"
166
+ f"Reason: {concise_reason}\n"
167
+ f"Override: /set yolo_mode true or /set safety_permission_level {risk_display}"
168
+ )
169
+ emit_info(error_msg)
170
+
171
+ # Return rejection info for the command runner
172
+ return {
173
+ "blocked": True,
174
+ "risk": assessment.risk,
175
+ "reasoning": assessment.reasoning,
176
+ "error_message": error_msg,
177
+ }
178
+
179
+ # Command is within acceptable risk threshold - remain silent
180
+ return None # Allow command to proceed
181
+
182
+ except Exception as e:
183
+ # On any error, fail safe by blocking the command
184
+ error_msg = (
185
+ f"🛑 Command blocked (risk HIGH > permission {threshold.upper()}).\n"
186
+ f"Reason: Safety assessment error: {str(e)}\n"
187
+ f"Override: /set yolo_mode true or /set safety_permission_level high"
188
+ )
189
+ return {
190
+ "blocked": True,
191
+ "risk": "high",
192
+ "reasoning": f"Safety assessment error: {str(e)}",
193
+ "error_message": error_msg,
194
+ }
195
+
196
+
197
+ def register():
198
+ """Register the shell safety callback."""
199
+ register_callback("run_shell_command", shell_safety_callback)
200
+
201
+
202
+ # Auto-register the callback when this module is imported
203
+ register()
@@ -0,0 +1 @@
1
+ """Stack-dump plugin: SIGUSR1 dumps every thread's stack to a log."""